Public: anyone can read this; the crew can change it.
Fast test/surfaces-adversarial.test.ts: chop and rewrite under the budget (1 s a test, 10 s a file) #12064
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/1338-fast-test-surfaces-adversarial-test-ts-c → main 1 commit fork
1 file
changed +24
−51
test/surfaces-adversarial.test.ts +24 −51
| @@ -23,6 +23,7 @@ import { openPull } from "../src/actions/pulls.ts"; | ||
| 23 | 23 | import { createApp, type App as Forge } from "../src/app.ts"; |
| 24 | 24 | import { addTeamMember, createOrg, createTeam } from "../src/core/orgs.ts"; |
| 25 | 25 | import type { Repo } from "../src/core/repos.ts"; |
| 26 | import { createSession } from "../src/core/sessions.ts"; | |
| 26 | 27 | import { createUser, getUserByName, type User } from "../src/core/users.ts"; |
| 27 | 28 | import { serve, type Servers } from "../src/http/server.ts"; |
| 28 | 29 | import { id } from "../src/kernel/id.ts"; |
| @@ -52,7 +53,6 @@ describe("surfaces adversarial: web", () => { | ||
| 52 | 53 | let servers: Servers; |
| 53 | 54 | let base = ""; |
| 54 | 55 | let alice = ""; |
| 55 | let alicePassword = "alice-adversarial-pass"; | |
| 56 | 56 | |
| 57 | 57 | const web = ( |
| 58 | 58 | cookie: string, |
| @@ -81,29 +81,13 @@ describe("surfaces adversarial: web", () => { | ||
| 81 | 81 | return await response.text(); |
| 82 | 82 | }; |
| 83 | 83 | |
| 84 | const signUp = async ( | |
| 85 | username: string, | |
| 86 | password = "a-good-passphrase-1", | |
| 87 | ): Promise<string> => { | |
| 88 | const response = await fetch(`${base}/signup`, { | |
| 89 | method: "POST", | |
| 90 | body: new URLSearchParams({ | |
| 91 | username, | |
| 92 | email: `${username}@forge.test`, | |
| 93 | password, | |
| 94 | }), | |
| 95 | redirect: "manual", | |
| 96 | }); | |
| 97 | return `${(response.headers.get("set-cookie") ?? "").split(";")[0]!}; forge_mode=dev`; | |
| 98 | }; | |
| 99 | ||
| 100 | 84 | beforeAll(async () => { |
| 101 | 85 | dataDir = await mkdtemp(join(tmpdir(), "forge-surfaces-adv-")); |
| 102 | 86 | // The standing sweep queries on a timer; the N+1 counts are of requests alone. |
| 103 | 87 | app = createApp({ dataDir, port: 0, standingSweepMs: 3_600_000 }); |
| 104 | 88 | servers = await serve(app); |
| 105 | 89 | base = servers.url; |
| 106 | alice = `${await adminCookie(app, { username: "alice", email: "[email protected]", password: alicePassword })}; forge_mode=dev`; | |
| 90 | alice = `${await adminCookie(app, { username: "alice", email: "[email protected]", password: "alice-adversarial-pass" })}; forge_mode=dev`; | |
| 107 | 91 | }, 30_000); |
| 108 | 92 | |
| 109 | 93 | afterAll(async () => { |
| @@ -325,6 +309,7 @@ describe("surfaces adversarial: web", () => { | ||
| 325 | 309 | // ---- 5. N+1 ---- |
| 326 | 310 | |
| 327 | 311 | test("N+1: /apps does not cost more queries as private, org-visible apps grow", async () => { |
| 312 | // Invariant: a listing of private org apps costs the same statements at 30 apps as at 5. Users here are passwordless with a session row (no argon2); 2 measured runs. | |
| 328 | 313 | const org = createOrg(app.db, "acme-apps", aliceUser().id); |
| 329 | 314 | expect(org.ok).toBe(true); |
| 330 | 315 | if (!org.ok) return; |
| @@ -334,14 +319,11 @@ describe("surfaces adversarial: web", () => { | ||
| 334 | 319 | const viewer = await createUser(app.db, { origin: "seed", trusted: true, |
| 335 | 320 | username: "viewer1", |
| 336 | 321 | email: "[email protected]", |
| 337 | password: "viewer-pass-1234", | |
| 338 | 322 | }); |
| 339 | 323 | expect(viewer.ok).toBe(true); |
| 340 | 324 | if (!viewer.ok) return; |
| 341 | 325 | expect(addTeamMember(app.db, team.value, viewer.value.id)).toBe(true); |
| 342 | const viewerCookie = await signUp("viewer1-cookie"); // unused path, just to reuse signUp's cookie header shape | |
| 343 | void viewerCookie; | |
| 344 | const cookie = await loginAs(base, "viewer1", "viewer-pass-1234"); | |
| 326 | const cookie = sessionFor(viewer.value.id); | |
| 345 | 327 | |
| 346 | 328 | const seedApps = (n: number): void => { |
| 347 | 329 | for (let i = 0; i < n; i++) { |
| @@ -367,20 +349,20 @@ describe("surfaces adversarial: web", () => { | ||
| 367 | 349 | const small = await statements(app.db, async () => { |
| 368 | 350 | const response = await web(cookie, "/apps"); |
| 369 | 351 | expect(response.status).toBe(200); |
| 370 | }); | |
| 352 | }, 2); | |
| 371 | 353 | seedApps(25); // 30 total now |
| 372 | 354 | const large = await statements(app.db, async () => { |
| 373 | 355 | const response = await web(cookie, "/apps"); |
| 374 | 356 | expect(response.status).toBe(200); |
| 375 | }); | |
| 357 | }, 2); | |
| 376 | 358 | expect(large).toBe(small); |
| 377 | 359 | }); |
| 378 | 360 | |
| 379 | 361 | test("N+1: the dashboard's Apps and On-you cards stay flat with 30 apps and 30 assigned issues", async () => { |
| 362 | // Invariant: the dashboard cards cost the same statements at 30 apps and 30 assigned issues as at 5. Users here are passwordless with a session row (no argon2); 2 measured runs. | |
| 380 | 363 | const owner = await createUser(app.db, { origin: "seed", trusted: true, |
| 381 | 364 | username: "dashowner", |
| 382 | 365 | email: "[email protected]", |
| 383 | password: "dashowner-pass-123", | |
| 384 | 366 | }); |
| 385 | 367 | expect(owner.ok).toBe(true); |
| 386 | 368 | if (!owner.ok) return; |
| @@ -393,12 +375,11 @@ describe("surfaces adversarial: web", () => { | ||
| 393 | 375 | const viewer = await createUser(app.db, { origin: "seed", trusted: true, |
| 394 | 376 | username: "dashviewer", |
| 395 | 377 | email: "[email protected]", |
| 396 | password: "dashviewer-pass-1", | |
| 397 | 378 | }); |
| 398 | 379 | expect(viewer.ok).toBe(true); |
| 399 | 380 | if (!viewer.ok) return; |
| 400 | 381 | expect(addTeamMember(app.db, team.value, viewer.value.id)).toBe(true); |
| 401 | const cookie = await loginAs(base, "dashviewer", "dashviewer-pass-1"); | |
| 382 | const cookie = sessionFor(viewer.value.id); | |
| 402 | 383 | |
| 403 | 384 | const seed = (n: number, offset: number): void => { |
| 404 | 385 | for (let i = 0; i < n; i++) { |
| @@ -438,12 +419,12 @@ describe("surfaces adversarial: web", () => { | ||
| 438 | 419 | const small = await statements(app.db, async () => { |
| 439 | 420 | const response = await web(cookie, "/"); |
| 440 | 421 | expect(response.status).toBe(200); |
| 441 | }); | |
| 422 | }, 2); | |
| 442 | 423 | seed(25, 5); // 30 total of each |
| 443 | 424 | const large = await statements(app.db, async () => { |
| 444 | 425 | const response = await web(cookie, "/"); |
| 445 | 426 | expect(response.status).toBe(200); |
| 446 | }); | |
| 427 | }, 2); | |
| 447 | 428 | expect(large).toBe(small); |
| 448 | 429 | }); |
| 449 | 430 | |
| @@ -453,10 +434,10 @@ describe("surfaces adversarial: web", () => { | ||
| 453 | 434 | // `writableRepoIds` query (`REPO_WRITABLE`, the SQL twin of `canWrite`), |
| 454 | 435 | // so the count must not move with the number of distinct repositories. |
| 455 | 436 | test("N+1: needsYouCard does not cost more as agent pulls span more distinct repos", async () => { |
| 437 | // Invariant: needsYouCard costs the same statements at 30 distinct repos as at 5. Users here are passwordless with a session row (no argon2); 2 measured runs. | |
| 456 | 438 | const owner = await createUser(app.db, { origin: "seed", trusted: true, |
| 457 | 439 | username: "needsowner", |
| 458 | 440 | email: "[email protected]", |
| 459 | password: "needsowner-pass-1", | |
| 460 | 441 | }); |
| 461 | 442 | if (!owner.ok) return; |
| 462 | 443 | const org = createOrg(app.db, "acme-needs", owner.value.id); |
| @@ -466,7 +447,6 @@ describe("surfaces adversarial: web", () => { | ||
| 466 | 447 | const viewer = await createUser(app.db, { origin: "seed", trusted: true, |
| 467 | 448 | username: "needsviewer", |
| 468 | 449 | email: "[email protected]", |
| 469 | password: "needsviewer-pass-1", | |
| 470 | 450 | }); |
| 471 | 451 | if (!viewer.ok) return; |
| 472 | 452 | addTeamMember(app.db, team.value, viewer.value.id); |
| @@ -474,7 +454,7 @@ describe("surfaces adversarial: web", () => { | ||
| 474 | 454 | username: "needsbot", |
| 475 | 455 | }); |
| 476 | 456 | if (!agent.ok) return; |
| 477 | const cookie = await loginAs(base, "needsviewer", "needsviewer-pass-1"); | |
| 457 | const cookie = sessionFor(viewer.value.id); | |
| 478 | 458 | |
| 479 | 459 | const seed = (n: number, offset: number): void => { |
| 480 | 460 | for (let i = 0; i < n; i++) { |
| @@ -501,15 +481,16 @@ describe("surfaces adversarial: web", () => { | ||
| 501 | 481 | await web(cookie, "/"); |
| 502 | 482 | const small = await statements(app.db, async () => { |
| 503 | 483 | await web(cookie, "/"); |
| 504 | }); | |
| 484 | }, 2); | |
| 505 | 485 | seed(25, 5); |
| 506 | 486 | const large = await statements(app.db, async () => { |
| 507 | 487 | await web(cookie, "/"); |
| 508 | }); | |
| 488 | }, 2); | |
| 509 | 489 | expect(large).toBe(small); |
| 510 | 490 | }); |
| 511 | 491 | |
| 512 | 492 | test("N+1: the runs list does not cost more at 50 rows than at 5", async () => { |
| 493 | // Invariant: the runs list costs the same statements at 50 rows as at 5. Users here are passwordless with a session row (no argon2); 2 measured runs. | |
| 513 | 494 | const madeRepo = await createRepo(app, aliceUser(), { |
| 514 | 495 | name: "runs-n1", |
| 515 | 496 | visibility: "public", |
| @@ -530,16 +511,17 @@ describe("surfaces adversarial: web", () => { | ||
| 530 | 511 | const small = await statements(app.db, async () => { |
| 531 | 512 | const response = await web(alice, "/alice/runs-n1/runs"); |
| 532 | 513 | expect(response.status).toBe(200); |
| 533 | }); | |
| 514 | }, 2); | |
| 534 | 515 | seedRuns(45); // 50 total |
| 535 | 516 | const large = await statements(app.db, async () => { |
| 536 | 517 | const response = await web(alice, "/alice/runs-n1/runs"); |
| 537 | 518 | expect(response.status).toBe(200); |
| 538 | }); | |
| 519 | }, 2); | |
| 539 | 520 | expect(large).toBe(small); |
| 540 | 521 | }); |
| 541 | 522 | |
| 542 | 523 | test("N+1: a pull's crew activity does not cost more as its agent runs grow", async () => { |
| 524 | // Invariant: a pull page costs the same statements however many agent runs it has. Users here are passwordless with a session row (no argon2); 2 measured runs. | |
| 543 | 525 | const agent = await createAgent(app, aliceUser(), { |
| 544 | 526 | username: "crewn1bot", |
| 545 | 527 | }); |
| @@ -598,7 +580,7 @@ describe("surfaces adversarial: web", () => { | ||
| 598 | 580 | `/alice/crew-n1/pulls/${opened.value.number}`, |
| 599 | 581 | ); |
| 600 | 582 | expect(response.status).toBe(200); |
| 601 | }); | |
| 583 | }, 2); | |
| 602 | 584 | seedActivity(20); |
| 603 | 585 | const large = await statements(app.db, async () => { |
| 604 | 586 | const response = await web( |
| @@ -606,7 +588,7 @@ describe("surfaces adversarial: web", () => { | ||
| 606 | 588 | `/alice/crew-n1/pulls/${opened.value.number}`, |
| 607 | 589 | ); |
| 608 | 590 | expect(response.status).toBe(200); |
| 609 | }); | |
| 591 | }, 2); | |
| 610 | 592 | expect(large).toBe(small); |
| 611 | 593 | }); |
| 612 | 594 | |
| @@ -630,24 +612,15 @@ describe("surfaces adversarial: web", () => { | ||
| 630 | 612 | expect(wide).toEqual([]); |
| 631 | 613 | }); |
| 632 | 614 | |
| 615 | // A passwordless seeded user signed in by session row: no argon2 per user. | |
| 616 | const sessionFor = (userId: string): string => | |
| 617 | `forge_session=${createSession(app.db, userId).token}; forge_mode=dev`; | |
| 618 | ||
| 633 | 619 | function aliceUser(): User { |
| 634 | 620 | return getUserByName(app.db, "alice")!; |
| 635 | 621 | } |
| 636 | 622 | }); |
| 637 | 623 | |
| 638 | /** Sign in over the API and return the session cookie's `cookie:` value. */ | |
| 639 | async function loginAs( | |
| 640 | base: string, | |
| 641 | username: string, | |
| 642 | password: string, | |
| 643 | ): Promise<string> { | |
| 644 | const response = await fetch(`${base}/signin`, { | |
| 645 | method: "POST", | |
| 646 | body: new URLSearchParams({ username, password }), | |
| 647 | redirect: "manual", | |
| 648 | }); | |
| 649 | return `${(response.headers.get("set-cookie") ?? "").split(";")[0]!}; forge_mode=dev`; | |
| 650 | } | |
| 651 | 624 | |
| 652 | 625 | // ============================================================================ |
| 653 | 626 | // TUI |
new above
offline