Skip to content

Commit 632e3d2

Browse files
committed
Refactor technical badge seeding and update condition handling for stat deltas
1 parent 1fc4b35 commit 632e3d2

9 files changed

Lines changed: 86 additions & 26 deletions

File tree

apps/backend/scripts/seed-technical-badges.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -332,26 +332,6 @@ async function seedTechnicalBadgesData() {
332332
},
333333
},
334334

335-
// Line 10: All comparison operators in one place
336-
{
337-
labelId,
338-
sequence: 10,
339-
content: "Testing all comparison operators.",
340-
contentType: "NARRATION",
341-
speakerId: null,
342-
visualType: "GENERATED",
343-
conditions: {
344-
stats: {
345-
strength: { value: 10, operator: ">=" },
346-
dexterity: { value: 5, operator: "<=" },
347-
intelligence: { value: 15, operator: ">" },
348-
wisdom: { value: 3, operator: "<" },
349-
charisma: { value: 8, operator: "==" },
350-
luck: { value: 0, operator: "!=" },
351-
},
352-
},
353-
},
354-
355335
// Line 9: Complex menu with condition flags
356336
{
357337
labelId,
@@ -376,6 +356,26 @@ async function seedTechnicalBadgesData() {
376356
},
377357
],
378358
},
359+
360+
// Line 10: All comparison operators in one place
361+
{
362+
labelId,
363+
sequence: 10,
364+
content: "Testing all comparison operators.",
365+
contentType: "NARRATION",
366+
speakerId: null,
367+
visualType: "GENERATED",
368+
conditions: {
369+
stats: {
370+
strength: { value: 10, operator: ">=" },
371+
dexterity: { value: 5, operator: "<=" },
372+
intelligence: { value: 15, operator: ">" },
373+
wisdom: { value: 3, operator: "<" },
374+
charisma: { value: 8, operator: "==" },
375+
luck: { value: 0, operator: "!=" },
376+
},
377+
},
378+
},
379379
];
380380

381381
await db.insert(labelLines).values(testLines);

apps/backend/src/services/__tests__/label-line-mapper.service.unit.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it, expect } from "vitest";
22
import { mapEntriesToLabelLineValues } from "../label-line-mapper";
3-
import type { ComparisonOperator } from "@branchforge/shared";
3+
import type { ComparisonOperator, StatCondition } from "@branchforge/shared";
44

55
describe("mapEntriesToLabelLineValues - technical metadata", () => {
66
const charactersByTag = new Map<string, string>();
@@ -118,6 +118,39 @@ describe("mapEntriesToLabelLineValues - technical metadata", () => {
118118
});
119119
});
120120

121+
it("preserves statDeltas when present with stats", () => {
122+
const entries = [
123+
{
124+
type: "DIALOGUE" as const,
125+
text: "Hello",
126+
speaker: "char1",
127+
lineNumber: 1,
128+
indentLevel: 0,
129+
conditions: {
130+
stats: {
131+
affection_luna: {
132+
value: 50,
133+
operator: ">=",
134+
} as StatCondition,
135+
},
136+
statDeltas: { affection_luna: 10 },
137+
},
138+
},
139+
];
140+
141+
const result = mapEntriesToLabelLineValues(
142+
entries,
143+
"label1",
144+
"project1",
145+
charactersByTag
146+
);
147+
148+
expect(result[0].conditions).toMatchObject({
149+
stats: { affection_luna: { value: 50, operator: ">=" } },
150+
statDeltas: { affection_luna: 10 },
151+
});
152+
});
153+
121154
it("maps null conditions when not present", () => {
122155
const entries = [
123156
{

apps/backend/src/services/__tests__/technical-parser.service.unit.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ describe("extractTechnicalConstructs - comparison operators", () => {
104104
});
105105
});
106106

107+
it("handles elif the same as if", () => {
108+
const rpyContent = "elif affection_luna >= 50:";
109+
const result = extractTechnicalConstructs(rpyContent, 0);
110+
expect(result.conditions).toEqual({
111+
stats: { affection_luna: { value: 50, operator: ">=" } },
112+
});
113+
});
114+
107115
it("captures <=, >, <, ==, != operators", () => {
108116
const rpyContent = `if strength <= 5 and magic > 10 and charm < 3 and mood == 2 and trust != 1:`;
109117
const result = extractTechnicalConstructs(rpyContent, 0);

apps/backend/src/services/label-line-mapper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type ContentType =
3333
export type LineConditions = {
3434
stats?: Record<string, number | StatCondition>;
3535
variables?: string[];
36+
statDeltas?: Record<string, number>;
3637
};
3738

3839
/**

apps/backend/src/services/labels.service.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
LabelStatus,
3131
sanitizeLabelName,
3232
RENPY_LABEL_REGEX,
33+
type ComparisonOperator,
3334
} from "@branchforge/shared";
3435
import { createAuditFields, updateAuditFields } from "../lib/audit.js";
3536
import {
@@ -1728,6 +1729,22 @@ export async function authorizeLabelAccess(
17281729
* @param label - The label data (with filePath from JOIN)
17291730
*/
17301731
function mapToPublicLabel(label: LabelForPublic): PublicLabel {
1732+
// Transform database format to API format for conditions.stats
1733+
// Database stores stats as Record<string, number>, API expects Record<string, StatCondition>
1734+
const transformedConditions: PublicLabel["conditions"] = label.conditions
1735+
? {
1736+
variables: label.conditions.variables,
1737+
stats: label.conditions.stats
1738+
? Object.fromEntries(
1739+
Object.entries(label.conditions.stats).map(([key, value]) => [
1740+
key,
1741+
{ value, operator: ">=" as ComparisonOperator },
1742+
])
1743+
)
1744+
: undefined,
1745+
}
1746+
: null;
1747+
17311748
return {
17321749
id: label.id,
17331750
projectId: label.projectId,
@@ -1742,7 +1759,7 @@ function mapToPublicLabel(label: LabelForPublic): PublicLabel {
17421759
visibility: label.visibility,
17431760
version: label.version,
17441761
contentHash: label.contentHash,
1745-
conditions: label.conditions ?? null,
1762+
conditions: transformedConditions,
17461763
projectFileId: label.projectFileId,
17471764
fileName: extractFileName(label.filePath),
17481765
createdAt: label.createdAt.toISOString(),

apps/frontend/src/components/__tests__/TechnicalPopover.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { render, screen } from "@testing-library/react";
2-
import { TechnicalPopover } from "../write-mode/TechnicalPopover";
2+
import { describe, it, expect } from "vitest";
3+
import { TechnicalPopover } from "@/components/write-mode/TechnicalPopover";
34
import type { StatCondition } from "@branchforge/shared";
45

56
describe("TechnicalPopover", () => {

apps/frontend/src/lib/api/__tests__/gitlab.unit.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { gitlabApi } from "../gitlab.js";
1010

1111
// Mock fetch
1212
const mockFetch = vi.fn();
13-
globalThis.fetch = mockFetch as unknown as typeof fetch;
13+
globalThis.fetch = mockFetch;
1414

1515
describe("GitLab API Client", () => {
1616
afterEach(() => {

apps/frontend/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"noUnusedLocals": true,
1616
"noUnusedParameters": true,
1717
"noFallthroughCasesInSwitch": true,
18-
"types": ["vitest/globals", "@testing-library/jest-dom"],
18+
"types": ["@testing-library/jest-dom"],
1919
"paths": {
2020
"@/*": ["./src/*"]
2121
}

packages/shared/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ export interface PublicLabel {
384384
/** Variable and stat conditions for this label */
385385
conditions: {
386386
variables?: string[];
387-
stats?: Record<string, number>;
387+
stats?: Record<string, StatCondition>;
388388
} | null;
389389
createdAt: string;
390390
updatedAt: string;

0 commit comments

Comments
 (0)