Build API v2
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { isHttpError } from '@sveltejs/kit';
|
||||
import { _clearRateLimits } from '$lib/server/v2-rate-limit';
|
||||
|
||||
vi.mock('$lib/db', () => ({
|
||||
prisma: {
|
||||
licenseKey: { findFirst: vi.fn() },
|
||||
user: { findUnique: vi.fn() },
|
||||
subscriptionLevel: { findFirst: vi.fn() }
|
||||
}
|
||||
}));
|
||||
|
||||
import { prisma } from '$lib/db';
|
||||
import { POST } from './+server';
|
||||
|
||||
type POSTEvent = Parameters<typeof POST>[0];
|
||||
|
||||
const mockKeyFindFirst = prisma.licenseKey.findFirst as ReturnType<typeof vi.fn>;
|
||||
const mockUserFindUnique = prisma.user.findUnique as ReturnType<typeof vi.fn>;
|
||||
const mockLevelFindFirst = prisma.subscriptionLevel.findFirst as ReturnType<typeof vi.fn>;
|
||||
|
||||
function licenseRequest(body: unknown): Request {
|
||||
return new Request('http://localhost/api/v2/license', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
}
|
||||
|
||||
async function expectHttpError(promise: unknown, status: number, message: string) {
|
||||
try {
|
||||
await promise;
|
||||
} catch (e) {
|
||||
expect(isHttpError(e)).toBe(true);
|
||||
const err = e as { status: number; body: { message: string } };
|
||||
expect(err.status).toBe(status);
|
||||
expect(err.body.message).toBe(message);
|
||||
return;
|
||||
}
|
||||
throw new Error('expected handler to throw an HttpError');
|
||||
}
|
||||
|
||||
const storedKey = {
|
||||
id: 'key-1',
|
||||
key: 'ABCD-EFGH-IJKL-MNOP',
|
||||
active: true,
|
||||
level: 'Advanced',
|
||||
userId: 'user-1'
|
||||
};
|
||||
|
||||
describe('POST /api/v2/license', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
_clearRateLimits();
|
||||
});
|
||||
|
||||
it('returns the subscription_level object for a valid key', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue(storedKey);
|
||||
mockUserFindUnique.mockResolvedValue({ id: 'user-1', email: 'user@example.com' });
|
||||
mockLevelFindFirst.mockResolvedValue({
|
||||
id: 2,
|
||||
name: 'Advanced',
|
||||
description: 'General + Specific'
|
||||
});
|
||||
|
||||
const res = await POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'user@example.com' })
|
||||
} as POSTEvent);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(await res.json()).toEqual({
|
||||
key: storedKey.key,
|
||||
email_address: 'user@example.com',
|
||||
subscription_level: { id: 2, name: 'Advanced', description: 'General + Specific' }
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to a structurally consistent subscription_level for an unknown level name', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue({ ...storedKey, level: 'SomeCustomLevel' });
|
||||
mockUserFindUnique.mockResolvedValue({ id: 'user-1', email: 'user@example.com' });
|
||||
mockLevelFindFirst.mockResolvedValue(null);
|
||||
|
||||
const res = await POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'user@example.com' })
|
||||
} as POSTEvent);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(await res.json()).toEqual({
|
||||
key: storedKey.key,
|
||||
email_address: 'user@example.com',
|
||||
subscription_level: { id: null, name: 'SomeCustomLevel', description: null }
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects a missing license_key', async () => {
|
||||
await expectHttpError(
|
||||
POST({ request: licenseRequest({ email_address: 'user@example.com' }) } as POSTEvent),
|
||||
400,
|
||||
'license_key is required'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a missing email_address', async () => {
|
||||
await expectHttpError(
|
||||
POST({ request: licenseRequest({ license_key: storedKey.key }) } as POSTEvent),
|
||||
400,
|
||||
'email_address is required'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects an unknown key', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue(null);
|
||||
await expectHttpError(
|
||||
POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'user@example.com' })
|
||||
} as POSTEvent),
|
||||
400,
|
||||
'license key not found'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects an inactive key', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue({ ...storedKey, active: false });
|
||||
await expectHttpError(
|
||||
POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'user@example.com' })
|
||||
} as POSTEvent),
|
||||
400,
|
||||
'license key is not active'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a nonexistent user', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue(storedKey);
|
||||
mockUserFindUnique.mockResolvedValue(null);
|
||||
await expectHttpError(
|
||||
POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'nobody@example.com' })
|
||||
} as POSTEvent),
|
||||
400,
|
||||
'user does not exist'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a key owned by a different user', async () => {
|
||||
mockKeyFindFirst.mockResolvedValue(storedKey);
|
||||
mockUserFindUnique.mockResolvedValue({ id: 'user-2', email: 'other@example.com' });
|
||||
await expectHttpError(
|
||||
POST({
|
||||
request: licenseRequest({ license_key: storedKey.key, email_address: 'other@example.com' })
|
||||
} as POSTEvent),
|
||||
400,
|
||||
'key does not belong to specified user'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a malformed license key format', async () => {
|
||||
await expectHttpError(
|
||||
POST({
|
||||
request: licenseRequest({ license_key: 'not-a-key', email_address: 'user@example.com' })
|
||||
} as POSTEvent),
|
||||
400,
|
||||
'license key not found'
|
||||
);
|
||||
expect(mockKeyFindFirst).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user