diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py index 6cf2806d56c0f48ac6af30517bf33869284e33c9..dc8763acd552c258d9bab1db243a506b8c62f8b2 100644 --- a/src/_zkapauthorizer/tests/strategies.py +++ b/src/_zkapauthorizer/tests/strategies.py @@ -61,6 +61,58 @@ from ..model import ( Voucher, ) +_POSIX_EPOCH = datetime.utcfromtimestamp(0) + + +def posix_safe_datetimes(): + """ + Build datetime instances in a range that can be represented as floats + without losing microsecond precision. + """ + return datetimes( + # I don't know that time-based parts of the system break down + # before the POSIX epoch but I don't know that they work, either. + # Don't time travel with this code. + min_value=_POSIX_EPOCH, + # Once we get far enough into the future we lose the ability to + # represent a timestamp with microsecond precision in a floating point + # number, which we do with any POSIX timestamp-like API (eg + # twisted.internet.task.Clock). So don't go far enough into the + # future. Furthermore, once we don't fit into an unsigned 4 byte + # integers, we can't round-trip through all the things that expect a + # time_t. Stay back from the absolute top to give tests a little + # space to advance time, too. + max_value=datetime.utcfromtimestamp(2 ** 31), + ) + + +def posix_timestamps(): + """ + Build floats in a range that can represent time without losing microsecond + precision. + """ + return posix_safe_datetimes().map( + lambda when: (when - _POSIX_EPOCH).total_seconds(), + ) + + +def clocks(now=posix_timestamps()): + """ + Build ``twisted.internet.task.Clock`` instances set to a time built by + ``now``. + + :param now: A strategy that builds POSIX timestamps (ie, ints or floats in + the range of time_t). + """ + + def clock_at_time(when): + c = Clock() + c.advance(when) + return c + + return now.map(clock_at_time) + + # Sizes informed by # https://github.com/brave-intl/challenge-bypass-ristretto/blob/2f98b057d7f353c12b2b12d0f5ae9ad115f1d0ba/src/oprf.rs#L18-L33 @@ -237,7 +289,10 @@ def zkapauthz_configuration( ``extra_configurations``. """ - def merge(extra_configuration, allowed_public_keys): + def merge( + extra_configuration, + allowed_public_keys, + ): config = { u"default-token-count": u"32", u"allowed-public-keys": u",".join(allowed_public_keys), @@ -266,21 +321,46 @@ def client_ristrettoredeemer_configurations(): ) -def client_dummyredeemer_configurations(): +def client_dummyredeemer_configurations( + crawl_means=one_of(none(), posix_timestamps()), + crawl_ranges=one_of(none(), posix_timestamps()), + min_times_remaining=one_of(none(), posix_timestamps()), +): """ Build DummyRedeemer-using configuration values for the client-side plugin. """ + def make_lease_config(crawl_mean, crawl_range, min_time_remaining): + config = {} + if crawl_mean is not None: + # Don't allow the mean to be 0 + config["lease.crawl-interval.mean"] = str(int(crawl_mean) + 1) + if crawl_range is not None: + config["lease.crawl-interval.range"] = str(int(crawl_range)) + if min_time_remaining is not None: + config["lease.min-time-remaining"] = str(int(min_time_remaining)) + return config + def share_a_key(allowed_keys): - return zkapauthz_configuration( - just( + lease_configs = builds( + make_lease_config, + crawl_means, + crawl_ranges, + min_times_remaining, + ) + extra_config = lease_configs.map( + lambda config: config.update( { u"redeemer": u"dummy", # Pick out one of the allowed public keys so that the dummy # appears to produce usable tokens. u"issuer-public-key": next(iter(allowed_keys)), } - ), + ) + or config, + ) + return zkapauthz_configuration( + extra_config, allowed_public_keys=just(allowed_keys), ) @@ -895,58 +975,6 @@ def announcements(): ) -_POSIX_EPOCH = datetime.utcfromtimestamp(0) - - -def posix_safe_datetimes(): - """ - Build datetime instances in a range that can be represented as floats - without losing microsecond precision. - """ - return datetimes( - # I don't know that time-based parts of the system break down - # before the POSIX epoch but I don't know that they work, either. - # Don't time travel with this code. - min_value=_POSIX_EPOCH, - # Once we get far enough into the future we lose the ability to - # represent a timestamp with microsecond precision in a floating point - # number, which we do with any POSIX timestamp-like API (eg - # twisted.internet.task.Clock). So don't go far enough into the - # future. Furthermore, once we don't fit into an unsigned 4 byte - # integers, we can't round-trip through all the things that expect a - # time_t. Stay back from the absolute top to give tests a little - # space to advance time, too. - max_value=datetime.utcfromtimestamp(2 ** 31), - ) - - -def posix_timestamps(): - """ - Build floats in a range that can represent time without losing microsecond - precision. - """ - return posix_safe_datetimes().map( - lambda when: (when - _POSIX_EPOCH).total_seconds(), - ) - - -def clocks(now=posix_timestamps()): - """ - Build ``twisted.internet.task.Clock`` instances set to a time built by - ``now``. - - :param now: A strategy that builds POSIX timestamps (ie, ints or floats in - the range of time_t). - """ - - def clock_at_time(when): - c = Clock() - c.advance(when) - return c - - return now.map(clock_at_time) - - @implementer(IFilesystemNode) @attr.s(frozen=True) class _LeafNode(object): diff --git a/src/_zkapauthorizer/tests/test_client_resource.py b/src/_zkapauthorizer/tests/test_client_resource.py index 490665f4e0b2cf12830e117eb6ef207888d737e0..292c505241c7a479b64300c4117a424cf61cf661 100644 --- a/src/_zkapauthorizer/tests/test_client_resource.py +++ b/src/_zkapauthorizer/tests/test_client_resource.py @@ -96,6 +96,7 @@ from .json import loads from .matchers import Provides, between, matches_response from .strategies import ( api_auth_tokens, + posix_timestamps, client_doublespendredeemer_configurations, client_dummyredeemer_configurations, client_errorredeemer_configurations, @@ -746,12 +747,15 @@ class UnblindedTokenTests(TestCase): using_a_token = after(getting_initial_tokens, use_a_token) getting_tokens_after = after(using_a_token, get_tokens) + def check_tokens(before_and_after): + initial_tokens, tokens_after = before_and_after + return initial_tokens[1:] == tokens_after + self.assertThat( gatherResults([getting_initial_tokens, getting_tokens_after]), succeeded( MatchesPredicate( - lambda (initial_tokens, tokens_after): initial_tokens[1:] - == tokens_after, + check_tokens, u"initial, after (%s): initial[1:] != after", ), ), @@ -1586,30 +1590,36 @@ class CalculatePriceTests(TestCase): ) @given( - # Make the share encoding parameters easily accessible without going - # through the Tahoe-LAFS configuration. - share_parameters().flatmap( - lambda params: tuples( - just(params), - tahoe_configs(shares=just(params)), + tuples( + # Make the share encoding parameters easily accessible without + # going through the Tahoe-LAFS configuration. + share_parameters(), + # Same goes for the minimum lease time remaining configuration. + posix_timestamps().map(int), + ).flatmap( + lambda share_and_lease_time: tuples( + just(share_and_lease_time), + direct_tahoe_configs( + zkapauthz_v1_configuration=client_dummyredeemer_configurations( + min_times_remaining=just(share_and_lease_time[1]), + ), + shares=just(share_and_lease_time[0]), + ), ), ), api_auth_tokens(), lists(integers(min_value=0)), ) - def test_calculated_price( - self, encoding_params_and_get_config, api_auth_token, sizes - ): + def test_calculated_price(self, encoding_params_and_config, api_auth_token, sizes): """ A well-formed request returns the price in ZKAPs as an integer and the storage period (the minimum allowed) that they pay for. """ - encoding_params, get_config = encoding_params_and_get_config + (encoding_params, min_time_remaining), config = encoding_params_and_config shares_needed, shares_happy, shares_total = encoding_params - - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, + add_api_token_to_config( + self.useFixture(TempDir()).join(b"tahoe"), + config, api_auth_token, ) root = root_from_config(config, datetime.now) @@ -1639,7 +1649,7 @@ class CalculatePriceTests(TestCase): Equals( { u"price": expected_price, - u"period": get_configured_lease_duration(config), + u"period": 60 * 60 * 24 * 31 - min_time_remaining, } ), ),