-
Notifications
You must be signed in to change notification settings - Fork 53
feat(task-board): run a column's rule when a card lands there, however it moved #6800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
4d15bfd
feat(task-board): run a column's rule when a card lands there, howeve…
viktormarinho 5920fb9
refactor(settings): one row per column, meaning and agent switch toge…
viktormarinho 4291fe9
fix(task-board): advance by the board's order, and say what the setti…
viktormarinho 1517f32
fix(settings): name the event Studio detects, not a paraphrase of it
viktormarinho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
apps/api/src/storage/task-board-column-claim.integration.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| /** | ||
| * Real-Postgres coverage for the fence a column rule claims through. | ||
| * | ||
| * The rule lives on a column, so the claim has to be conditional on the card | ||
| * still sitting in THAT column. It used to be conditional on the board's queue | ||
| * lane instead, while the rule itself was looked up by the card's status — so | ||
| * a rule on any other column found itself, tried to claim somewhere else, | ||
| * matched nothing, and did nothing. Silently, because a lost claim and an | ||
| * impossible one both come back null. | ||
| */ | ||
|
|
||
| import { afterAll, beforeAll, describe, expect, it } from "bun:test"; | ||
| import { sql } from "kysely"; | ||
| import { SUPER_AGENT_ASSIGNEE_ID } from "@decocms/shared/task-board"; | ||
| import type { StudioDatabase } from "../database"; | ||
| import { | ||
| closeTestPgDatabase, | ||
| connectTestPgDatabase, | ||
| resetTestPgDatabase, | ||
| } from "../database/test-db-pg"; | ||
| import { TaskBoardStorage } from "./task-board"; | ||
|
|
||
| const ORG = "org_column_claim"; | ||
| const USER = "user_column_claim"; | ||
|
|
||
| describe("claimUnassignedForSuperAgent (real Postgres)", () => { | ||
| let database: StudioDatabase; | ||
| let taskBoard: TaskBoardStorage; | ||
|
|
||
| beforeAll(async () => { | ||
| database = await connectTestPgDatabase(); | ||
| await resetTestPgDatabase(database); | ||
| await database.db | ||
| .insertInto("organization") | ||
| .values({ | ||
| id: ORG, | ||
| name: ORG, | ||
| slug: "org-column-claim", | ||
| createdAt: new Date().toISOString(), | ||
| }) | ||
| .execute(); | ||
| const now = new Date().toISOString(); | ||
| await sql` | ||
| INSERT INTO "user" (id, email, "emailVerified", name, "createdAt", "updatedAt") | ||
| VALUES (${USER}, ${"column-claim@test"}, false, ${USER}, ${now}, ${now}) | ||
| `.execute(database.db); | ||
| taskBoard = new TaskBoardStorage(database.db); | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| await closeTestPgDatabase(database); | ||
| }); | ||
|
|
||
| const card = (status: string) => | ||
| taskBoard.create({ | ||
| organizationId: ORG, | ||
| title: `card in ${status}`, | ||
| status, | ||
| by: USER, | ||
| }); | ||
|
|
||
| /** | ||
| * The bug. A tracker's column is called whatever it is called, and a team | ||
| * putting its rule on "QA" is the normal case — not everything starts from | ||
| * a queue lane. | ||
| */ | ||
| it("claims a card in the column the rule is on, whatever it is called", async () => { | ||
| const task = await card("QA Deco"); | ||
| const claimed = await taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| "QA Deco", | ||
| ); | ||
| expect(claimed?.assigneeId).toBe(SUPER_AGENT_ASSIGNEE_ID); | ||
| expect(claimed?.assignedBy).toBe(USER); | ||
| }); | ||
|
|
||
| /** The card moved on between the read and the claim, so the rule that fired | ||
| * is no longer the rule for where it is. */ | ||
| it("refuses a card that has left that column", async () => { | ||
| const task = await card("Fazendo"); | ||
| expect( | ||
| await taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| "QA Deco", | ||
| ), | ||
| ).toBeNull(); | ||
| }); | ||
|
|
||
| /** What makes it a fence: exactly one of two concurrent triggers wins, so a | ||
| * card cannot buy two agent runs. */ | ||
| it("lets one of two concurrent triggers win, never both", async () => { | ||
| const task = await card("Fazendo"); | ||
| const [a, b] = await Promise.all([ | ||
| taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| "Fazendo", | ||
| ), | ||
| taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| "Fazendo", | ||
| ), | ||
| ]); | ||
| expect([a, b].filter(Boolean)).toHaveLength(1); | ||
| }); | ||
|
|
||
| /** A card someone already owns is theirs; a rule never takes it. */ | ||
| it("refuses a card that already has an assignee", async () => { | ||
| const task = await card("Fazendo"); | ||
| await taskBoard.update(task.id, ORG, { assigneeId: USER }, USER); | ||
| expect( | ||
| await taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| "Fazendo", | ||
| ), | ||
| ).toBeNull(); | ||
| }); | ||
|
|
||
| /** Null is "this board has no such column", which cannot be claimed in. */ | ||
| it("refuses when the board has no column for the rule", async () => { | ||
| const task = await card("Fazendo"); | ||
| expect( | ||
| await taskBoard.claimUnassignedForSuperAgent( | ||
| task.id, | ||
| ORG, | ||
| USER, | ||
| USER, | ||
| null, | ||
| ), | ||
| ).toBeNull(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: When Jira moves an issue between statuses grouped by the same column, this call re-runs the column rule even though the card never landed in a new column. Gate the Jira path on
before?.status !== statuswhile retaining the create-path behavior.Prompt for AI agents