Skip to main content

max / synckit

Observe Cents arithmetic and the subscription wire round trip Assert get, the saturating add at i64::MAX, and Display against literals. Add an integration module driving get_subscription_status and queue_storage_cap_change against wiremock, checking the parsed interval, current and pending caps rather than only that the call succeeded.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 22:00 UTC
Signed with PGP, not checked
Commit: 943c269da075edbb850bd61d89c3067469343907
Parent: a4146a4
3 files changed, +89 insertions, -0 deletions
@@ -444,6 +444,16 @@
444 444 );
445 445 }
446 446
447 + #[test]
448 + fn cents_accessor_saturation_and_display() {
449 + assert_eq!(Cents(7).get(), 7);
450 + // The whole point of the newtype: an overflowing add saturates instead
451 + // of panicking in debug or wrapping negative in release.
452 + assert_eq!(Cents(i64::MAX).saturating_add(Cents(1)), Cents(i64::MAX));
453 + // Display is the bare cent count, matching the transparent wire form.
454 + assert_eq!(format!("{}", Cents(1234)), "1234");
455 + }
456 +
447 457 #[test]
448 458 fn cents_is_wire_transparent_to_i64() {
449 459 assert_eq!(serde_json::to_string(&Cents(1234)).unwrap(), "1234");
@@ -26,5 +26,6 @@
26 26 #[cfg(not(feature = "keychain"))]
27 27 mod rotation;
28 28 mod subscribe;
29 + mod subscription;
29 30 mod sync;
30 31 mod transport;
@@ -1,0 +1,78 @@
1 + //! Subscription status and the queued storage-cap change.
2 + //!
3 + //! Both calls return the same `SubscriptionStatus` shape, and both are only
4 + //! useful if the fields survive the round trip: `active` gates whether an app
5 + //! syncs at all, and `pending_storage_limit_bytes` is the whole answer to
6 + //! "did the cap change take". Asserting `Ok` alone would pass on a default
7 + //! `SubscriptionStatus`.
8 +
9 + use crate::common::*;
10 + use synckit_client::BillingInterval;
11 +
12 + const SUBSCRIPTION_PATH: &str = "/api/v1/sync/subscription";
13 + const STORAGE_CAP_PATH: &str = "/api/v1/sync/subscription/storage-cap";
14 +
15 + #[tokio::test]
16 + async fn subscription_status_parses_every_field() {
17 + let kit = MockKit::start().await;
18 + kit.get(SUBSCRIPTION_PATH)
19 + .json(json!({
20 + "active": true,
21 + // The interval travels under the legacy `tier` field name.
22 + "tier": "annual",
23 + "status": "active",
24 + "storage_limit_bytes": 10_737_418_240i64,
25 + "pending_storage_limit_bytes": serde_json::Value::Null,
26 + "storage_used_bytes": 4_096i64,
27 + "current_period_end": "2026-09-01T00:00:00Z",
28 + }))
29 + .await;
30 +
31 + let status = kit
32 + .authed()
33 + .get_subscription_status()
34 + .await
35 + .expect("a 200 with a full body parses");
36 +
37 + assert!(status.active);
38 + assert_eq!(status.interval, Some(BillingInterval::Annual));
39 + assert_eq!(status.status.as_deref(), Some("active"));
40 + assert_eq!(status.storage_limit_bytes, Some(10_737_418_240));
41 + assert_eq!(status.pending_storage_limit_bytes, None);
42 + assert_eq!(status.storage_used_bytes, Some(4_096));
43 + assert_eq!(
44 + status.current_period_end.as_deref(),
45 + Some("2026-09-01T00:00:00Z")
46 + );
47 + }
48 +
49 + #[tokio::test]
50 + async fn queue_storage_cap_change_sends_the_cap_and_reads_back_the_pending_one() {
51 + let kit = MockKit::start().await;
52 + kit.post(STORAGE_CAP_PATH)
53 + .json(json!({
54 + "active": true,
55 + "tier": "monthly",
56 + "status": "active",
57 + "storage_limit_bytes": 10_737_418_240i64,
58 + // The queued cap applies at the next cycle, so the current limit is
59 + // unchanged and this is the only field carrying the new number.
60 + "pending_storage_limit_bytes": 21_474_836_480i64,
61 + "storage_used_bytes": 4_096i64,
62 + "current_period_end": "2026-09-01T00:00:00Z",
63 + }))
64 + .await;
65 +
66 + let status = kit
67 + .authed()
68 + .queue_storage_cap_change(21_474_836_480)
69 + .await
70 + .expect("a 200 with a full body parses");
71 +
72 + assert_eq!(status.interval, Some(BillingInterval::Monthly));
73 + assert_eq!(status.storage_limit_bytes, Some(10_737_418_240));
74 + assert_eq!(status.pending_storage_limit_bytes, Some(21_474_836_480));
75 +
76 + let body = kit.body(STORAGE_CAP_PATH).await;
77 + assert_eq!(body["cap_bytes"], 21_474_836_480i64);
78 + }