feat: productionize learning engine and classroom

This commit is contained in:
inman
2026-08-16 21:44:52 +08:00
parent 2d04197f3f
commit ba35adfbfa
124 changed files with 9071 additions and 796 deletions

View File

@@ -4,6 +4,7 @@ import { middleware } from '@/middleware';
afterEach(() => {
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
describe('ACCESS_CODE middleware deployment boundary', () => {
@@ -54,7 +55,7 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
expect(response.headers.get('x-middleware-next')).toBe('1');
});
test('allows a single-course generation POST while hiding the ownerless global job list', async () => {
test('hides both single-course generation POST and its ownerless global job list', async () => {
vi.stubEnv('NODE_ENV', 'production');
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
@@ -65,7 +66,7 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
new NextRequest('https://server.example/api/generate-classroom'),
);
expect(createResponse.headers.get('x-middleware-next')).toBe('1');
expect(createResponse.status).toBe(404);
expect(listResponse.status).toBe(404);
});
@@ -98,7 +99,7 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
expect(response.headers.get('x-middleware-next')).toBeNull();
});
test('does not preempt a server publish request authenticated by Bearer at the route', async () => {
test('does not expose legacy publish APIs even when a caller supplies a Bearer', async () => {
vi.stubEnv('NODE_ENV', 'production');
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
vi.stubEnv('ACCESS_CODE', 'shared-compose-access-code');
@@ -110,8 +111,8 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
}),
);
expect(response.status).toBe(200);
expect(response.headers.get('x-middleware-next')).toBe('1');
expect(response.status).toBe(404);
expect(response.headers.get('x-middleware-next')).toBeNull();
});
test('preserves learner handling for ordinary and operations paths', async () => {
@@ -129,7 +130,7 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
expect(opsApi.status).toBe(403);
});
test('preserves operations workbench routing', async () => {
test('serves the operations shell but fails closed for APIs without Works identity config', async () => {
vi.stubEnv('NODE_ENV', 'test');
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'ops');
vi.stubEnv('ACCESS_CODE', '');
@@ -138,6 +139,43 @@ describe('ACCESS_CODE middleware deployment boundary', () => {
const api = await middleware(new NextRequest('https://ops.example/api/courses'));
expect(page.headers.get('x-middleware-next')).toBe('1');
expect(api.headers.get('x-middleware-next')).toBe('1');
expect(api.status).toBe(503);
});
test.each([
'/api/generate-classroom',
'/api/quiz-grade',
'/api/pbl/v2/instructor',
])('requires an introspected Works admin for every ops API: %s', async (pathname) => {
vi.stubEnv('NODE_ENV', 'production');
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'ops');
vi.stubEnv('WORKS_SQUARE_API_BASE_URL', 'https://works.example');
vi.stubEnv('OPS_PUBLIC_ORIGIN', 'https://ops.example');
const unauthorized = await middleware(new NextRequest(`https://ops.example${pathname}`, {
method: 'POST',
headers: { origin: 'https://ops.example' },
}));
expect(unauthorized.status).toBe(401);
const introspection = vi.fn(async () => Response.json({
username: 'jiaoyuop',
site_role: 'admin',
}));
vi.stubGlobal('fetch', introspection);
const authorized = await middleware(new NextRequest(`https://ops.example${pathname}`, {
method: 'POST',
headers: {
origin: 'https://ops.example',
authorization: 'Bearer works-session',
},
}));
expect(authorized.headers.get('x-middleware-next')).toBe('1');
expect(introspection).toHaveBeenCalledWith(
'https://works.example/api/auth/me',
expect.objectContaining({
headers: expect.objectContaining({ Authorization: 'Bearer works-session' }),
}),
);
});
});