1e6bacd9a98d
close the joint test's review: prove the explicit offer, every field, and the clock-order prune (v3.0.67)
- time
- 2026-09-27 16:17 UTC · 2 weeks ago
- machine
- Junes-MacBook-Pro.local
- agent
- deepseek/deepseek-flash
- session
- kepr connect and public steal
- parent
- fe8c5250fc91
↓ kepr-1e6bacd9.tar.gzatlas at this save
files changed4 files
package.json+1 −1tests/connect.test.ts+19 −0tests/kohremote.test.ts+7 −2tests/kohsteal.test.ts+10 −6package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "kepr",3- "version": "3.0.66",3+ "version": "3.0.67",44 "private": true,55 "type": "module",66 "scripts": {tests/connect.test.ts+19 −0
@@ -242,6 +242,25 @@242242 expect((await redeem(app, fresh, "192.0.2.40")).status).toBe(200);243243 });244244 245+ test("the attempt window is counted by time, not by the list's order", async () => {246+ const { app, browser } = await connected();247+ const address = "192.0.2.70";248+ // Nine attempts at one moment...249+ for (let i = 0; i < 9; i++) await redeem(app, "bad", address);250+ // ...then the clock steps back — an NTP correction, or a test's fake timer — and a tenth251+ // attempt lands *older* than the nine before it. A prune that walks the list by position252+ // stops at the first entry still inside the window and never reaches that one.253+ clock -= 120_000;254+ setSystemTime(clock);255+ await redeem(app, "bad", address);256+ // Now: an hour and two minutes after the nine, so the older tenth has left the window while257+ // they are still inside it — the peer has one attempt left, not none.258+ clock += 3_620_000;259+ setSystemTime(clock);260+ const { code } = await issue(browser, "after the step");261+ expect((await redeem(app, code, address)).status).toBe(200);262+ });263+245264 test("a forged X-Forwarded-For does not buy another bucketful", async () => {246265 const { app, browser } = await connected();247266 const { code } = await issue(browser);tests/kohremote.test.ts+7 −2
@@ -80,11 +80,14 @@8080 8181 test("the save and object prefixes list what a clone walks", async () => {8282 const saves = await get("saves");83+ expect(saves.status).toBe(200);8384 expect(saves.headers.get("cache-control")).toBe("no-cache");8485 expect((await saves.text()).trim().split("\n")).toContain(head);8586 const save = await record(head);86- const objects = await (await get("objects")).text();87- expect(objects.trim().split("\n")).toContain(save.files[0].hash);87+ const objects = await get("objects");88+ expect(objects.status).toBe(200);89+ expect(objects.headers.get("cache-control")).toBe("no-cache");90+ expect((await objects.text()).trim().split("\n")).toContain(save.files[0].hash);8891 });8992 9093 test("an object is the stored zlib bytes and hashes back to its key", async () => {@@ -129,6 +132,8 @@129132 "format",130133 "head",131134 "events",135+ "saves",136+ "objects",132137 `saves/${head}`,133138 `objects/${"a".repeat(64)}`,134139 `events/${"b".repeat(12)}`,tests/kohsteal.test.ts+10 −6
@@ -73,10 +73,9 @@7373 return { code, out, err };7474 }7575 76+/** The whole line, every field of every save: what a clone must reproduce exactly. */7677 async function line(repo: string, home: string): Promise<unknown> {77- return JSON.parse((await koh3(repo, home, ["view", "--json"])).out).saves.map(78- (save: { id: string; message: string }) => [save.id, save.message],79- );78+ return JSON.parse((await koh3(repo, home, ["view", "--json"])).out).saves;8079 }8180 8281 describe.serial("stealing a public repo from Kepr over HTTP", () => {@@ -225,11 +224,16 @@225224 const upgraded = await koh3(clone, thiefHome, ["offer"]);226225 expect(upgraded.code, upgraded.err + upgraded.out).toBe(0);227226 expect(upgraded.out + upgraded.err).toContain("now syncs with your bucket");227+ // The owner can read what that offer published, before anything else is saved: the228+ // local save the clone was carrying went up with it.229+ expect((await koh3(repoDir, ownerHome, ["revert", "--yes"])).code).toBe(0);230+ expect((await koh3(repoDir, ownerHome, ["steal"])).code).toBe(0);231+ expect(await readFile(join(repoDir, "local.txt"), "utf8")).toContain("work in progress");232+ expect(JSON.parse((await koh3(repoDir, ownerHome, ["view", "--json"])).out).saves).toEqual(233+ JSON.parse((await koh3(clone, thiefHome, ["view", "--json"])).out).saves,234+ );228235 await writeFile(join(clone, "README.md"), "# shipped\n\nThe thief's edit.\n");229236 expect((await koh3(clone, thiefHome, ["save", "from the thief"])).code).toBe(0);230- // The owner steals it back from the bucket; the stripped file is unsaved work there231- // too, so it goes first.232- expect((await koh3(repoDir, ownerHome, ["revert", "--yes"])).code).toBe(0);233237 expect((await koh3(repoDir, ownerHome, ["steal"])).code).toBe(0);234238 expect(await readFile(join(repoDir, "README.md"), "utf8")).toContain("The thief's edit");235239