Skip to content

Commit 7ac5698

Browse files
authored
Merge pull request #4231 from Kilo-Org/mark/unique-approval-file-numbers
feat(test-llm-autocompletion): use globally unique file numbers for approvals
2 parents ce76e5c + 4286be0 commit 7ac5698

File tree

445 files changed

+1261
-1235
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

445 files changed

+1261
-1235
lines changed

src/test-llm-autocompletion/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ This test suite uses approval testing instead of regex pattern matching to valid
2121
2222
- Display the test input and output
2323
- Ask you whether the output is acceptable
24-
- Save your decision to `approvals/{category}/{test-name}/approved.N.txt` or `rejected.N.txt`
24+
- Save your decision to `approvals/{category}/{test-name}.approved.N.txt` or `{test-name}.rejected.N.txt`
25+
- File numbers are globally unique across approved and rejected files (e.g., `approved.1.txt`, `rejected.2.txt`, `approved.3.txt`)
2526
2627
2. **Subsequent Runs**:
2728
- If the output matches a previously approved file, the test passes
@@ -195,6 +196,6 @@ Is this acceptable? (y/n):
195196
## Notes
196197

197198
- The `approvals/` directory is gitignored
198-
- Each approved/rejected output gets a unique numbered file
199+
- Each approved/rejected output gets a globally unique numbered file (numbers are unique across both approved and rejected files for the same test case)
199200
- Tests only prompt for input in the terminal when output is new
200201
- The test summary at the end shows how many passed/failed

src/test-llm-autocompletion/approvals.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,30 @@ describe("approvals", () => {
8080

8181
expect(files).toContain(`${TEST_NAME}.rejected.1.txt`)
8282
})
83+
84+
it("should use globally unique numbers across approved and rejected", async () => {
85+
let callCount = 0
86+
vi.spyOn(readline, "createInterface").mockReturnValue({
87+
question: (_prompt: string, callback: (answer: string) => void) => {
88+
// First call: approve, second call: reject, third call: approve
89+
callCount++
90+
callback(callCount === 2 ? "n" : "y")
91+
},
92+
close: () => {},
93+
} as any)
94+
95+
await checkApproval(TEST_CATEGORY, TEST_NAME, "input1", "output1") // approved.1
96+
await checkApproval(TEST_CATEGORY, TEST_NAME, "input2", "output2") // rejected.2
97+
await checkApproval(TEST_CATEGORY, TEST_NAME, "input3", "output3") // approved.3
98+
99+
const categoryDir = path.join(TEST_APPROVALS_DIR, TEST_CATEGORY)
100+
const files = fs.readdirSync(categoryDir)
101+
102+
expect(files).toContain(`${TEST_NAME}.approved.1.txt`)
103+
expect(files).toContain(`${TEST_NAME}.rejected.2.txt`)
104+
expect(files).toContain(`${TEST_NAME}.approved.3.txt`)
105+
expect(files).toHaveLength(3)
106+
})
83107
})
84108

85109
describe("matching existing files", () => {

src/test-llm-autocompletion/approvals.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@ function getCategoryPath(category: string): string {
2828
return path.join(APPROVALS_DIR, category)
2929
}
3030

31-
function getNextFileNumber(categoryDir: string, testName: string, type: "approved" | "rejected"): number {
31+
function getNextFileNumber(categoryDir: string, testName: string): number {
3232
if (!fs.existsSync(categoryDir)) {
3333
return 1
3434
}
3535

3636
const files = fs.readdirSync(categoryDir)
37-
const pattern = new RegExp(`^${testName}\\.${type}\\.(\\d+)\\.txt$`)
37+
// Match both approved and rejected files to get globally unique numbers
38+
const pattern = new RegExp(`^${testName}\\.(approved|rejected)\\.(\\d+)\\.txt$`)
3839
const numbers = files
3940
.filter((f) => pattern.test(f))
4041
.map((f) => {
4142
const match = f.match(pattern)
42-
return match ? parseInt(match[1], 10) : 0
43+
return match ? parseInt(match[2], 10) : 0
4344
})
4445

4546
return numbers.length > 0 ? Math.max(...numbers) + 1 : 1
@@ -136,7 +137,7 @@ export async function checkApproval(
136137

137138
fs.mkdirSync(categoryDir, { recursive: true })
138139

139-
const nextNumber = getNextFileNumber(categoryDir, testName, type)
140+
const nextNumber = getNextFileNumber(categoryDir, testName)
140141
const filename = `${testName}.${type}.${nextNumber}.txt`
141142
const filePath = path.join(categoryDir, filename)
142143

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.10.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ class ApiClient {
77
},
88
body: JSON.stringify(data),
99
});
10+
1011
if (!response.ok) {
1112
throw new Error(`HTTP error! status: ${response.status}`);
1213
}
13-
return response.json();
14+
15+
return await response.json();
1416
}
1517
}

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.11.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ class ApiClient {
77
},
88
body: JSON.stringify(data),
99
});
10-
return response.json();
10+
if (!response.ok) {
11+
throw new Error(`HTTP error! status: ${response.status}`);
12+
}
13+
return await response.json();
1114
}
1215
}

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.12.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ class ApiClient {
77
},
88
body: JSON.stringify(data),
99
});
10-
const result = await response.json();
11-
return result;
12-
}
10+
return await response.json();
11+
}
12+
}

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.13.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ class ApiClient {
1010
if (!response.ok) {
1111
throw new Error(`HTTP error! status: ${response.status}`);
1212
}
13-
return await response.json();
14-
}
13+
return response.json();
14+
}
15+
}

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.14.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ class ApiClient {
77
},
88
body: JSON.stringify(data),
99
});
10-
const json = await response.json();
11-
return json;
12-
}
10+
return response.json();
11+
}
12+
}

src/test-llm-autocompletion/approvals/api-patterns/rest-api-client.approved.15.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ class ApiClient {
66
'Content-Type': 'application/json',
77
},
88
body: JSON.stringify(data),
9-
});
9+
});
10+
const result = await response.json();
11+
return result;
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class ApiClient {
2+
async post(endpoint, data) {
3+
const response = await fetch(this.baseUrl + endpoint, {
4+
method: 'POST',
5+
headers: {
6+
'Content-Type': 'application/json',
7+
},
8+
body: JSON.stringify(data),
9+
});
10+
if (!response.ok) {
11+
throw new Error(`HTTP error! status: ${response.status}`);
12+
}
13+
return await response.json();
14+
}

0 commit comments

Comments
 (0)