-
Notifications
You must be signed in to change notification settings - Fork 3
Add /v1/* API route aliases while preserving legacy endpoints
#63
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,6 +87,238 @@ function createProcess(mocks = {}) { | |
| return proc; | ||
| } | ||
|
|
||
| test('Process serves notice on legacy /GetNotice endpoint', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/GetNotice', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'test notice' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
|
Comment on lines
+101
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Add assertions on HTTP status (and possibly headers) to more fully validate the behavior of the new aliased endpoints. For these new tests (e.g. Suggested implementation: const response = await proc.Process();
assert.strictEqual(response.status, 200);
assert.match(response.headers.get('content-type') ?? '', /^application\/json\b/i);
const payload = await response.json();
assert.strictEqual(payload.Success, true);
assert.strictEqual(payload.Data.Notice, 'test notice');
|
||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Notice, 'test notice'); | ||
| }); | ||
|
|
||
| test('Process serves notice on versioned /v1/GetNotice endpoint', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/v1/GetNotice', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'test notice' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Notice, 'test notice'); | ||
| }); | ||
|
|
||
| test('Process maps /v1 to GetNotice for versioned default endpoint', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/v1', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'test notice' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Notice, 'test notice'); | ||
| }); | ||
|
|
||
| test('Process uses GetStd C++ string processing on /v1/GetStd', async () => { | ||
| const req = new Request('https://example.com/v1/GetStd', { | ||
| method: 'POST', | ||
| headers: { | ||
| "CF-Connecting-IP": "127.0.0.1", | ||
| "content-type": "application/json" | ||
| }, | ||
| body: JSON.stringify({ | ||
| Authentication: { SessionID: 'testsession', Username: 'testuser' }, | ||
| Data: {}, | ||
|
Comment on lines
+141
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Consider adding a negative/edge-case test for versioned GetStd to ensure aliasing doesn’t bypass error handling. You’re already verifying that Suggested implementation: const payload = await response.json();
assert.strictEqual(payload.Success, true);
assert.strictEqual(payload.Data.Notice, 'test notice');
});
test('Process uses GetStd C++ error handling on /v1/GetStd for failing C++ processing', async () => {
const req = new Request('https://example.com/v1/GetStd', {
method: 'POST',
headers: {
"CF-Connecting-IP": "127.0.0.1",
"content-type": "application/json"
},
body: JSON.stringify({
Authentication: { SessionID: 'testsession', Username: 'testuser' },
Data: {},
Version: 'test',
}),
});
const proc = new Process(req, {
CheckToken: async () => ({
Success: true,
Data: { Username: 'testuser' },
}),
ProcessFunctions: {
GetStd: {
// Simulate a C++-side error path for GetStd
post: async () => {
return {
ok: false,
status: 500,
json: async () => ({
Success: false,
Error: 'forced C++ GetStd failure',
}),
};
},
},
},
});
const response = await proc.Process();
const payload = await response.json();
// Ensure the alias /v1/GetStd follows the same C++ error path as /GetStd
assert.strictEqual(payload.Success, false);
assert.strictEqual(payload.Error, 'forced C++ GetStd failure');
});
test('Process uses GetStd C++ string processing on /v1/GetStd', async () => {You may need to adjust the new test to match your existing testing conventions and error shapes:
|
||
| Version: 'test', | ||
| DebugMode: false | ||
| }) | ||
| }); | ||
| const proc = createProcess({ req }); | ||
|
|
||
| proc.CheckToken = test.mock.fn(async () => new Result(true, '', { Success: true })); | ||
| proc.ProcessFunctions.GetStd = test.mock.fn(async () => new Result(true, 'ok', { Code: 'line1\nline2' })); | ||
|
|
||
| const response = await proc.Process(); | ||
| const responseText = await response.text(); | ||
| const expectedText = proc.processCppString(JSON.stringify(new Result(true, 'ok', { Code: 'line1\nline2' }))); | ||
|
|
||
| assert.strictEqual(responseText, expectedText); | ||
| }); | ||
|
|
||
| test('Process routes a generic versioned endpoint to its legacy handler', async () => { | ||
| const req = new Request('https://example.com/v1/GetUserSettings', { | ||
| method: 'POST', | ||
| headers: { | ||
| "CF-Connecting-IP": "127.0.0.1", | ||
| "content-type": "application/json" | ||
| }, | ||
| body: JSON.stringify({ | ||
| Authentication: { SessionID: 'testsession', Username: 'testuser' }, | ||
| Data: {}, | ||
| Version: 'test', | ||
| DebugMode: false | ||
| }) | ||
| }); | ||
| const proc = createProcess({ req }); | ||
|
|
||
| proc.CheckToken = test.mock.fn(async () => new Result(true, '', { Success: true })); | ||
| proc.ProcessFunctions.GetUserSettings = test.mock.fn(async () => new Result(true, 'ok', { Settings: { Discussion: 'true' } })); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
|
|
||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Message, 'ok'); | ||
| assert.deepStrictEqual(payload.Data.Settings, { Discussion: 'true' }); | ||
| }); | ||
|
|
||
| test('Process maps root / to GetNotice', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'root notice' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Notice, 'root notice'); | ||
| }); | ||
|
|
||
| test('Process maps /v1/ trailing slash to GetNotice', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/v1/', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'v1 slash notice' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Notice, 'v1 slash notice'); | ||
| }); | ||
|
|
||
| test('Process serves addon script on legacy /GetAddOnScript endpoint', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/GetAddOnScript', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'console.log("addon");' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Script, 'console.log("addon");'); | ||
| }); | ||
|
|
||
| test('Process serves addon script on versioned /v1/GetAddOnScript endpoint', async () => { | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/v1/GetAddOnScript', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }), | ||
| kv: { | ||
| get: async () => 'console.log("addon v1");' | ||
| } | ||
| }); | ||
|
|
||
| const response = await proc.Process(); | ||
| const payload = await response.json(); | ||
| assert.strictEqual(payload.Success, true); | ||
| assert.strictEqual(payload.Data.Script, 'console.log("addon v1");'); | ||
| }); | ||
|
|
||
| test('Process serves image on legacy GET /GetImage endpoint', async () => { | ||
| const imageData = new Uint8Array([137, 80, 78, 71]).buffer; | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/GetImage?ImageID=test.png', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }) | ||
| }); | ||
|
|
||
| proc.ProcessFunctions.GetImage = test.mock.fn(async () => new Blob([imageData], { type: 'image/png' })); | ||
|
Comment on lines
+262
to
+271
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Strengthen the GetImage tests by verifying the response body, not just headers and arguments. Right now the test only checks that Suggested implementation: test('Process serves image on legacy GET /GetImage endpoint', async () => {
const imageData = new Uint8Array([137, 80, 78, 71]).buffer;
const proc = createProcess({
req: new Request('https://example.com/GetImage?ImageID=test.png', {
method: 'GET',
headers: { 'CF-Connecting-IP': '127.0.0.1' }
})
});
proc.ProcessFunctions.GetImage = test.mock.fn(
async () => new Blob([imageData], { type: 'image/png' })
);
const response = await proc.Process();
// Validate the handler was invoked with the expected arguments
assert.strictEqual(proc.ProcessFunctions.GetImage.mock.calls.length, 1);
assert.deepStrictEqual(proc.ProcessFunctions.GetImage.mock.calls[0][0], {
ImageID: 'test.png'
});
// Validate response headers
assert.strictEqual(response.headers.get('content-type'), 'image/png');
// Validate that the response body matches the image data we set up
const bodyBuffer = await response.arrayBuffer();
assert.strictEqual(bodyBuffer.byteLength, imageData.byteLength);
assert.deepStrictEqual(
new Uint8Array(bodyBuffer),
new Uint8Array(imageData)
);
});If your test framework exposes mock call metadata differently than |
||
|
|
||
| const response = await proc.Process(); | ||
| assert.strictEqual(response.headers.get('content-type'), 'image/png'); | ||
| assert.strictEqual(proc.ProcessFunctions.GetImage.mock.calls.length, 1); | ||
| assert.deepStrictEqual(proc.ProcessFunctions.GetImage.mock.calls[0].arguments[0], { ImageID: 'test.png' }); | ||
| }); | ||
|
|
||
| test('Process serves image on versioned GET /v1/GetImage endpoint', async () => { | ||
| const imageData = new Uint8Array([137, 80, 78, 71]).buffer; | ||
| const proc = createProcess({ | ||
| req: new Request('https://example.com/v1/GetImage?ImageID=v1-test.png', { | ||
| method: 'GET', | ||
| headers: { "CF-Connecting-IP": "127.0.0.1" } | ||
| }) | ||
| }); | ||
|
|
||
| proc.ProcessFunctions.GetImage = test.mock.fn(async () => new Blob([imageData], { type: 'image/png' })); | ||
|
|
||
| const response = await proc.Process(); | ||
| assert.strictEqual(response.headers.get('content-type'), 'image/png'); | ||
| assert.strictEqual(proc.ProcessFunctions.GetImage.mock.calls.length, 1); | ||
| assert.deepStrictEqual(proc.ProcessFunctions.GetImage.mock.calls[0].arguments[0], { ImageID: 'v1-test.png' }); | ||
| }); | ||
|
|
||
| test('Process uses GetStd C++ string processing on legacy /GetStd', async () => { | ||
| const req = new Request('https://example.com/GetStd', { | ||
| method: 'POST', | ||
| headers: { | ||
| "CF-Connecting-IP": "127.0.0.1", | ||
| "content-type": "application/json" | ||
| }, | ||
| body: JSON.stringify({ | ||
| Authentication: { SessionID: 'testsession', Username: 'testuser' }, | ||
| Data: {}, | ||
| Version: 'test', | ||
| DebugMode: false | ||
| }) | ||
| }); | ||
| const proc = createProcess({ req }); | ||
|
|
||
| proc.CheckToken = test.mock.fn(async () => new Result(true, '', { Success: true })); | ||
| proc.ProcessFunctions.GetStd = test.mock.fn(async () => new Result(true, 'ok', { Code: 'int main() {\n\treturn 0;\n}' })); | ||
|
|
||
| const response = await proc.Process(); | ||
| const responseText = await response.text(); | ||
| const expectedText = proc.processCppString(JSON.stringify(new Result(true, 'ok', { Code: 'int main() {\n\treturn 0;\n}' }))); | ||
|
|
||
| assert.strictEqual(responseText, expectedText); | ||
| }); | ||
|
|
||
| test('CheckParams passes with valid data', () => { | ||
| const proc = createProcess(); | ||
| const result = proc.CheckParams({ a: 1, b: 'x' }, { a: 'number', b: 'string' }); | ||
|
|
||
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.
suggestion (testing): Add a negative test for the versioned /v1/notify path to mirror the legacy unauthorized-case coverage.
To fully mirror the legacy behavior and protect against regressions in the aliasing, please also add a test where
/v1/notifyis called with a missing or invalidX-Notification-Token, asserting it returns401and does not send anything on the socket, matching the existing/notifyunauthorized test.Suggested implementation:
body: JSON.stringify(...),assert.strictEqual(...), andassert.deepStrictEqual(...)lines) exactly matches what is already in your file; adjust indentation or trailing commas as needed./notifyunauthorized test uses a different payload shape or additional assertions, you may want to mirror those details in the new/v1/notifyunauthorized test for stricter parity.