diff --git a/server/src/modules/kobo/kobo-device.controller.test.ts b/server/src/modules/kobo/kobo-device.controller.test.ts index 02b8dc435..80bd5509e 100644 --- a/server/src/modules/kobo/kobo-device.controller.test.ts +++ b/server/src/modules/kobo/kobo-device.controller.test.ts @@ -1,3 +1,6 @@ +import { RequestMethod } from '@nestjs/common'; +import { METHOD_METADATA, PATH_METADATA } from '@nestjs/common/constants'; + import { KoboDeviceController } from './kobo-device.controller'; function makeReply() { @@ -160,14 +163,34 @@ describe('KoboDeviceController', () => { expect(thumbnailService.serveThumbnail).toHaveBeenCalledWith(11, 420, undefined, reply); }); - it('returns static payload endpoints and analytics keys', async () => { + it('returns static payload endpoints and ingests analytics events', async () => { expect(controller.affiliate()).toEqual({}); expect(controller.remainingBookSeries()).toEqual({ TotalResultCount: 0, SearchResults: [] }); expect(controller.productNextRead()).toEqual([]); analyticsService.ingest.mockResolvedValue(undefined); await expect(controller.analyticsEvent({ Events: [] }, { id: 3 } as never, { deviceToken: 't' } as never)).resolves.toEqual({}); expect(analyticsService.ingest).toHaveBeenCalledWith({ Events: [] }, { id: 3 }, { deviceToken: 't' }); - expect(controller.getTests()).toEqual({ Result: 'Success', TestKey: expect.any(String) }); + }); + + it('registers GET and POST handlers for the advertised gettests endpoint', () => { + expect(Reflect.getMetadata(PATH_METADATA, KoboDeviceController.prototype.getTests)).toBe('v1/analytics/gettests'); + expect(Reflect.getMetadata(METHOD_METADATA, KoboDeviceController.prototype.getTests)).toBe(RequestMethod.GET); + expect(Reflect.getMetadata(PATH_METADATA, KoboDeviceController.prototype.postGetTests)).toBe('v1/analytics/gettests'); + expect(Reflect.getMetadata(METHOD_METADATA, KoboDeviceController.prototype.postGetTests)).toBe(RequestMethod.POST); + }); + + it.each([ + ['GET', () => controller.getTests()], + ['POST', () => controller.postGetTests()], + ])('returns a Kobo-compatible success payload for %s gettests requests', (_method, request) => { + const response = request(); + + expect(response).toEqual({ + Result: 'Success', + TestKey: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/), + Tests: {}, + }); + expect(proxyService.forward).not.toHaveBeenCalled(); }); it('returns {} when analytics ingest throws', async () => { diff --git a/server/src/modules/kobo/kobo-device.controller.ts b/server/src/modules/kobo/kobo-device.controller.ts index 37390b0bb..a904b0d7a 100644 --- a/server/src/modules/kobo/kobo-device.controller.ts +++ b/server/src/modules/kobo/kobo-device.controller.ts @@ -119,10 +119,16 @@ export class KoboDeviceController { return []; } - @Post('v1/analytics/gettests') + @Get('v1/analytics/gettests') @HttpCode(HttpStatus.OK) getTests() { - return { Result: 'Success', TestKey: randomUUID() }; + return this.buildGetTestsResponse(); + } + + @Post('v1/analytics/gettests') + @HttpCode(HttpStatus.OK) + postGetTests() { + return this.buildGetTestsResponse(); } @Post('v1/analytics/event') @@ -143,6 +149,10 @@ export class KoboDeviceController { await this.proxyService.forward(req, reply, device.deviceToken); } + private buildGetTestsResponse() { + return { Result: 'Success', TestKey: randomUUID(), Tests: {} }; + } + private async serveThumbnailOrProxy( bookId: string, ifNoneMatch: string | undefined, diff --git a/server/test/e2e/authorization-matrix/route-inventory/manifest.json b/server/test/e2e/authorization-matrix/route-inventory/manifest.json index 2c59ea510..47ac04ac6 100644 --- a/server/test/e2e/authorization-matrix/route-inventory/manifest.json +++ b/server/test/e2e/authorization-matrix/route-inventory/manifest.json @@ -1,7 +1,7 @@ { - "totalRoutes": 367, + "totalRoutes": 368, "byPermission": { - "(none)": 164, + "(none)": 165, "Permission.ManageMetadataConfig": 32, "Permission.EmailSend": 30, "Permission.LibraryEditMetadata": 31, diff --git a/server/test/e2e/authorization-matrix/route-inventory/routes-04.json b/server/test/e2e/authorization-matrix/route-inventory/routes-04.json index 250376cde..dc3d33c9d 100644 --- a/server/test/e2e/authorization-matrix/route-inventory/routes-04.json +++ b/server/test/e2e/authorization-matrix/route-inventory/routes-04.json @@ -195,6 +195,18 @@ "file": "server/src/modules/kobo/kobo-device.controller.ts", "className": "KoboDeviceController", "methodName": "getTests", + "httpMethod": "GET", + "path": "/kobo/:deviceToken/v1/analytics/gettests", + "permissions": [], + "libraryAccess": [], + "isPublic": true, + "allowDefault": false, + "useGuards": ["KoboTokenGuard"] + }, + { + "file": "server/src/modules/kobo/kobo-device.controller.ts", + "className": "KoboDeviceController", + "methodName": "postGetTests", "httpMethod": "POST", "path": "/kobo/:deviceToken/v1/analytics/gettests", "permissions": [],