@substrat-run/contract-tests 0.146.0 → 0.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8472,6 +8472,111 @@ export function scopeHostContractSuite(adapterName, makeFixture, opts = {}) {
8472
8472
  expect(fleet.storage.bytes).toBe(fleet.perTenant.reduce((n, r) => n + (r.storage?.bytes ?? 0), 0));
8473
8473
  expect(fleet.storage.sampled).toBe(fleet.perTenant.reduce((n, r) => n + (r.storage?.sampled ?? 0), 0));
8474
8474
  });
8475
+ it('folds scope-store samples into the gauge, and drops them past retention (#2212)', async () => {
8476
+ const ta = tenantId.parse(ulid());
8477
+ await host.admin.createTenant(staff, { id: ta, slug: 'gauge-stores', name: 'Gauge Stores' });
8478
+ const s = await newScope(ta);
8479
+ await host.admin.recordScopeStorage(staff, [{
8480
+ tenantId: ta,
8481
+ scopeId: s,
8482
+ bytes: 100,
8483
+ readAt: day(0, '08:00:00'),
8484
+ stores: [{ binding: 'GRAPH', bytes: 40 }, { binding: 'BLOBS', bytes: 2 }],
8485
+ }]);
8486
+ expect((await host.admin.readMeters(staff, { tenantId: ta })).perTenant[0].storage?.stores).toEqual({
8487
+ bytes: 42,
8488
+ sampled: 2,
8489
+ });
8490
+ // The day's reading is replaced, so a binding that is gone is not still billed.
8491
+ await host.admin.recordScopeStorage(staff, [{
8492
+ tenantId: ta,
8493
+ scopeId: s,
8494
+ bytes: 100,
8495
+ readAt: day(0, '09:00:00'),
8496
+ stores: [{ binding: 'GRAPH', bytes: 7 }],
8497
+ }]);
8498
+ expect((await host.admin.readMeters(staff, { tenantId: ta })).perTenant[0].storage?.stores).toEqual({
8499
+ bytes: 7,
8500
+ sampled: 1,
8501
+ });
8502
+ // A later day that no longer names a binding stops billing it. Yesterday's row
8503
+ // stays until retention; the latest day that measured stores is the one read.
8504
+ const vanished = tenantId.parse(ulid());
8505
+ await host.admin.createTenant(staff, { id: vanished, slug: 'gauge-stores-gone', name: 'Gauge Stores Gone' });
8506
+ const gone = await newScope(vanished);
8507
+ await host.admin.recordScopeStorage(staff, [{
8508
+ tenantId: vanished,
8509
+ scopeId: gone,
8510
+ bytes: 100,
8511
+ readAt: day(-1, '08:00:00'),
8512
+ stores: [{ binding: 'GRAPH', bytes: 40 }, { binding: 'BLOBS', bytes: 2 }],
8513
+ }]);
8514
+ await host.admin.recordScopeStorage(staff, [{
8515
+ tenantId: vanished,
8516
+ scopeId: gone,
8517
+ bytes: 100,
8518
+ readAt: day(0, '08:00:00'),
8519
+ stores: [{ binding: 'GRAPH', bytes: 7 }],
8520
+ }]);
8521
+ expect((await host.admin.readMeters(staff, { tenantId: vanished })).perTenant[0].storage?.stores).toEqual({
8522
+ bytes: 7,
8523
+ sampled: 1,
8524
+ });
8525
+ // A later day that measured no store at all (a version that dropped its only binding)
8526
+ // stops billing every binding: an empty reading is recorded, not skipped.
8527
+ const emptied = tenantId.parse(ulid());
8528
+ await host.admin.createTenant(staff, { id: emptied, slug: 'gauge-stores-empty', name: 'Gauge Stores Empty' });
8529
+ const empty = await newScope(emptied);
8530
+ await host.admin.recordScopeStorage(staff, [{
8531
+ tenantId: emptied,
8532
+ scopeId: empty,
8533
+ bytes: 100,
8534
+ readAt: day(-1, '08:00:00'),
8535
+ stores: [{ binding: 'GRAPH', bytes: 40 }, { binding: 'BLOBS', bytes: 2 }],
8536
+ }]);
8537
+ await host.admin.recordScopeStorage(staff, [{ tenantId: emptied, scopeId: empty, bytes: 100, readAt: day(0, '08:00:00'), stores: [] }]);
8538
+ expect((await host.admin.readMeters(staff, { tenantId: emptied })).perTenant[0].storage).toMatchObject({ bytes: 100, sampled: 1 });
8539
+ expect((await host.admin.readMeters(staff, { tenantId: emptied })).perTenant[0].storage?.stores).toBeUndefined();
8540
+ // An older reading of a day, recorded after a newer one, leaves the newer one, as the
8541
+ // database sample does.
8542
+ await host.admin.recordScopeStorage(staff, [{
8543
+ tenantId: ta,
8544
+ scopeId: s,
8545
+ bytes: 100,
8546
+ readAt: day(0, '08:30:00'),
8547
+ stores: [{ binding: 'GRAPH', bytes: 99 }, { binding: 'BLOBS', bytes: 1 }],
8548
+ }]);
8549
+ expect((await host.admin.readMeters(staff, { tenantId: ta })).perTenant[0].storage?.stores).toEqual({
8550
+ bytes: 7,
8551
+ sampled: 1,
8552
+ });
8553
+ const old = new Date();
8554
+ old.setUTCMonth(old.getUTCMonth() - 14);
8555
+ const aged = tenantId.parse(ulid());
8556
+ await host.admin.createTenant(staff, { id: aged, slug: 'gauge-stores-old', name: 'Gauge Stores Old' });
8557
+ const agedScope = await newScope(aged);
8558
+ await host.admin.recordScopeStorage(staff, [{
8559
+ tenantId: aged,
8560
+ scopeId: agedScope,
8561
+ bytes: 1,
8562
+ readAt: old.toISOString(),
8563
+ stores: [{ binding: 'GRAPH', bytes: 9 }],
8564
+ }]);
8565
+ expect((await host.admin.readMeters(staff, { tenantId: aged })).perTenant[0].storage?.stores).toEqual({
8566
+ bytes: 9,
8567
+ sampled: 1,
8568
+ });
8569
+ // One call deletes at most the limit, shared by the database table and the store table.
8570
+ expect(await host.admin.pruneScopeStorage(staff, 1)).toBe(1);
8571
+ while ((await host.admin.pruneScopeStorage(staff, 500)) > 0)
8572
+ ;
8573
+ expect((await host.admin.readMeters(staff, { tenantId: aged })).perTenant[0].storage?.stores).toBeUndefined();
8574
+ // The recent tenant's sample is inside retention, so the prune leaves it.
8575
+ expect((await host.admin.readMeters(staff, { tenantId: ta })).perTenant[0].storage?.stores).toEqual({
8576
+ bytes: 7,
8577
+ sampled: 1,
8578
+ });
8579
+ });
8475
8580
  it("forgets a reaped scope's samples, and never writes one for it again", async () => {
8476
8581
  const tr = tenantId.parse(ulid());
8477
8582
  await host.admin.createTenant(staff, { id: tr, slug: 'gauge-reap', name: 'Gauge Reap' });