fix(registry): restore protocol routing and test coverage for npm, oci, and api flows
This commit is contained in:
@@ -15,9 +15,12 @@ import {
|
||||
createTestApiToken,
|
||||
createTestRepository,
|
||||
createTestUser,
|
||||
getTestRegistry,
|
||||
runCommand,
|
||||
setupTestDb,
|
||||
skipIfMissing,
|
||||
startTestServer,
|
||||
stopTestServer,
|
||||
teardownTestDb,
|
||||
testConfig,
|
||||
} from '../helpers/index.ts';
|
||||
@@ -27,7 +30,7 @@ const FIXTURE_DIR = path.join(
|
||||
'../fixtures/npm/@stack-test/demo-package',
|
||||
);
|
||||
|
||||
describe('NPM E2E: Full lifecycle', () => {
|
||||
describe('NPM E2E: Full lifecycle', { sanitizeResources: false, sanitizeOps: false }, () => {
|
||||
let testUserId: string;
|
||||
let testOrgName: string;
|
||||
let apiToken: string;
|
||||
@@ -41,11 +44,13 @@ describe('NPM E2E: Full lifecycle', () => {
|
||||
|
||||
await setupTestDb();
|
||||
registryUrl = testConfig.registry.url;
|
||||
await startTestServer();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (!shouldSkip) {
|
||||
await teardownTestDb();
|
||||
await stopTestServer();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,6 +59,24 @@ describe('NPM E2E: Full lifecycle', () => {
|
||||
|
||||
await cleanupTestDb();
|
||||
|
||||
// Clean up S3 test packages from previous runs
|
||||
try {
|
||||
const bucket = getTestRegistry()?.getSmartBucket();
|
||||
if (bucket) {
|
||||
const b = await bucket.getBucketByName(testConfig.s3.bucket);
|
||||
if (b) {
|
||||
for (const key of [
|
||||
'npm/packages/@stack-test/demo-package/index.json',
|
||||
'npm/packages/@stack-test/demo-package/stack-test-demo-package-1.0.0.tgz',
|
||||
]) {
|
||||
await b.fastRemove({ path: key }).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore S3 cleanup errors
|
||||
}
|
||||
|
||||
// Create test user and org
|
||||
const { user } = await createTestUser({ status: 'active' });
|
||||
testUserId = user.id;
|
||||
@@ -237,11 +260,17 @@ describe('NPM E2E: Full lifecycle', () => {
|
||||
apiToken,
|
||||
);
|
||||
|
||||
// Unpublish
|
||||
const unpublishResult = await clients.npm.unpublish(
|
||||
'@stack-test/demo-package@1.0.0',
|
||||
`${registryUrl}/-/npm/${testOrgName}/`,
|
||||
apiToken,
|
||||
// Unpublish (run from FIXTURE_DIR so .npmrc auth is picked up)
|
||||
const unpublishResult = await runCommand(
|
||||
[
|
||||
'npm',
|
||||
'unpublish',
|
||||
'@stack-test/demo-package@1.0.0',
|
||||
'--registry',
|
||||
`${registryUrl}/-/npm/${testOrgName}/`,
|
||||
'--force',
|
||||
],
|
||||
{ cwd: FIXTURE_DIR },
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
createTestUser,
|
||||
setupTestDb,
|
||||
skipIfMissing,
|
||||
startTestServer,
|
||||
stopTestServer,
|
||||
teardownTestDb,
|
||||
testConfig,
|
||||
} from '../helpers/index.ts';
|
||||
@@ -26,7 +28,7 @@ const FIXTURE_DIR = path.join(
|
||||
'../fixtures/oci',
|
||||
);
|
||||
|
||||
describe('OCI E2E: Full lifecycle', () => {
|
||||
describe('OCI E2E: Full lifecycle', { sanitizeResources: false, sanitizeOps: false }, () => {
|
||||
let testUserId: string;
|
||||
let testOrgName: string;
|
||||
let apiToken: string;
|
||||
@@ -41,11 +43,13 @@ describe('OCI E2E: Full lifecycle', () => {
|
||||
await setupTestDb();
|
||||
const url = new URL(testConfig.registry.url);
|
||||
registryHost = url.host;
|
||||
await startTestServer();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (!shouldSkip) {
|
||||
await teardownTestDb();
|
||||
await stopTestServer();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -85,7 +89,7 @@ describe('OCI E2E: Full lifecycle', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageName = `${registryHost}/v2/${testOrgName}/demo:1.0.0`;
|
||||
const imageName = `${registryHost}/${testOrgName}/demo:1.0.0`;
|
||||
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.simple');
|
||||
|
||||
try {
|
||||
@@ -112,7 +116,7 @@ describe('OCI E2E: Full lifecycle', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageName = `${registryHost}/v2/${testOrgName}/demo:1.0.0`;
|
||||
const imageName = `${registryHost}/${testOrgName}/demo:1.0.0`;
|
||||
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.simple');
|
||||
|
||||
try {
|
||||
@@ -138,7 +142,7 @@ describe('OCI E2E: Full lifecycle', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageName = `${registryHost}/v2/${testOrgName}/multi:1.0.0`;
|
||||
const imageName = `${registryHost}/${testOrgName}/multi:1.0.0`;
|
||||
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.multi-layer');
|
||||
|
||||
try {
|
||||
|
||||
@@ -81,5 +81,8 @@ export {
|
||||
setupTestStorage,
|
||||
} from './storage.helper.ts';
|
||||
|
||||
// Server helpers
|
||||
export { getTestRegistry, startTestServer, stopTestServer } from './server.helper.ts';
|
||||
|
||||
// Re-export test config
|
||||
export { getTestConfig, testConfig } from '../test.config.ts';
|
||||
|
||||
49
test/helpers/server.helper.ts
Normal file
49
test/helpers/server.helper.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Server helper - starts/stops the registry server for integration and E2E tests
|
||||
*/
|
||||
|
||||
import { StackGalleryRegistry } from '../../ts/registry.ts';
|
||||
import { testConfig } from '../test.config.ts';
|
||||
|
||||
let registry: StackGalleryRegistry | null = null;
|
||||
|
||||
/**
|
||||
* Start the registry server for testing
|
||||
*/
|
||||
export async function startTestServer(): Promise<StackGalleryRegistry> {
|
||||
if (registry) return registry;
|
||||
|
||||
// Set JWT_SECRET env var so ApiRouter's AuthService uses the same secret
|
||||
Deno.env.set('JWT_SECRET', testConfig.jwt.secret);
|
||||
|
||||
registry = new StackGalleryRegistry({
|
||||
mongoUrl: testConfig.mongodb.url,
|
||||
mongoDb: testConfig.mongodb.name,
|
||||
s3Endpoint: testConfig.s3.endpoint,
|
||||
s3AccessKey: testConfig.s3.accessKey,
|
||||
s3SecretKey: testConfig.s3.secretKey,
|
||||
s3Bucket: testConfig.s3.bucket,
|
||||
s3Region: testConfig.s3.region,
|
||||
port: testConfig.registry.port,
|
||||
jwtSecret: testConfig.jwt.secret,
|
||||
});
|
||||
await registry.start();
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the registry server
|
||||
*/
|
||||
export async function stopTestServer(): Promise<void> {
|
||||
if (registry) {
|
||||
await registry.stop();
|
||||
registry = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current registry instance
|
||||
*/
|
||||
export function getTestRegistry(): StackGalleryRegistry | null {
|
||||
return registry;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export const clients = {
|
||||
docker: {
|
||||
check: () => commandExists('docker'),
|
||||
build: (dockerfile: string, tag: string, context: string) =>
|
||||
runCommand(['docker', 'build', '-f', dockerfile, '-t', tag, context]),
|
||||
runCommand(['docker', 'build', '--load', '-f', dockerfile, '-t', tag, context]),
|
||||
push: (image: string) => runCommand(['docker', 'push', image]),
|
||||
pull: (image: string) => runCommand(['docker', 'pull', image]),
|
||||
rmi: (image: string, force = false) =>
|
||||
|
||||
@@ -13,16 +13,20 @@ import {
|
||||
get,
|
||||
post,
|
||||
setupTestDb,
|
||||
startTestServer,
|
||||
stopTestServer,
|
||||
teardownTestDb,
|
||||
} from '../helpers/index.ts';
|
||||
|
||||
describe('Auth API Integration', () => {
|
||||
describe('Auth API Integration', { sanitizeResources: false, sanitizeOps: false }, () => {
|
||||
beforeAll(async () => {
|
||||
await setupTestDb();
|
||||
await startTestServer();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await teardownTestDb();
|
||||
await stopTestServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -56,7 +60,7 @@ describe('Auth API Integration', () => {
|
||||
|
||||
assertStatus(response, 401);
|
||||
const body = response.body as Record<string, unknown>;
|
||||
assertEquals(body.error, 'INVALID_CREDENTIALS');
|
||||
assertEquals(body.code, 'INVALID_CREDENTIALS');
|
||||
});
|
||||
|
||||
it('should return 401 for inactive user', async () => {
|
||||
@@ -72,7 +76,7 @@ describe('Auth API Integration', () => {
|
||||
|
||||
assertStatus(response, 401);
|
||||
const body = response.body as Record<string, unknown>;
|
||||
assertEquals(body.error, 'ACCOUNT_INACTIVE');
|
||||
assertEquals(body.code, 'ACCOUNT_INACTIVE');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -155,9 +159,14 @@ describe('Auth API Integration', () => {
|
||||
});
|
||||
const loginBody = loginResponse.body as Record<string, unknown>;
|
||||
const token = loginBody.accessToken as string;
|
||||
const sessionId = loginBody.sessionId as string;
|
||||
|
||||
// Logout
|
||||
const logoutResponse = await post('/api/v1/auth/logout', {}, createAuthHeader(token));
|
||||
// Logout with sessionId
|
||||
const logoutResponse = await post(
|
||||
'/api/v1/auth/logout',
|
||||
{ sessionId },
|
||||
createAuthHeader(token),
|
||||
);
|
||||
|
||||
assertStatus(logoutResponse, 200);
|
||||
|
||||
|
||||
@@ -16,19 +16,23 @@ import {
|
||||
post,
|
||||
put,
|
||||
setupTestDb,
|
||||
startTestServer,
|
||||
stopTestServer,
|
||||
teardownTestDb,
|
||||
} from '../helpers/index.ts';
|
||||
|
||||
describe('Organization API Integration', () => {
|
||||
describe('Organization API Integration', { sanitizeResources: false, sanitizeOps: false }, () => {
|
||||
let accessToken: string;
|
||||
let testUserId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupTestDb();
|
||||
await startTestServer();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await teardownTestDb();
|
||||
await stopTestServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -116,8 +120,8 @@ describe('Organization API Integration', () => {
|
||||
const response = await get('/api/v1/organizations', createAuthHeader(accessToken));
|
||||
|
||||
assertStatus(response, 200);
|
||||
const body = response.body as Record<string, unknown>[];
|
||||
assertEquals(body.length >= 2, true);
|
||||
const body = response.body as { organizations: Record<string, unknown>[] };
|
||||
assertEquals(body.organizations.length >= 2, true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,8 +206,8 @@ describe('Organization API Integration', () => {
|
||||
);
|
||||
|
||||
assertStatus(response, 200);
|
||||
const body = response.body as Record<string, unknown>[];
|
||||
assertEquals(body.length >= 1, true); // At least the creator
|
||||
const body = response.body as { members: Record<string, unknown>[] };
|
||||
assertEquals(body.members.length >= 1, true); // At least the creator
|
||||
});
|
||||
|
||||
it('should add member to organization', async () => {
|
||||
|
||||
Reference in New Issue
Block a user