- Implemented unit tests for the Package model, covering methods such as generateId, findById, findByName, and version management. - Created unit tests for the Repository model, including repository creation, name validation, and retrieval methods. - Added tests for the Session model, focusing on session creation, validation, and invalidation. - Developed unit tests for the User model, ensuring user creation, password hashing, and retrieval methods function correctly. - Implemented AuthService tests, validating login, token refresh, and session management. - Added TokenService tests, covering token creation, validation, and revocation processes.
191 lines
5.4 KiB
TypeScript
191 lines
5.4 KiB
TypeScript
/**
|
|
* OCI Protocol E2E Tests
|
|
*
|
|
* Tests the full OCI container image lifecycle: push -> pull -> delete
|
|
* Requires: docker CLI, running registry, Docker test infrastructure
|
|
*/
|
|
|
|
import { assertEquals } from 'jsr:@std/assert';
|
|
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
|
|
import * as path from '@std/path';
|
|
import {
|
|
setupTestDb,
|
|
teardownTestDb,
|
|
cleanupTestDb,
|
|
createTestUser,
|
|
createOrgWithOwner,
|
|
createTestRepository,
|
|
createTestApiToken,
|
|
clients,
|
|
skipIfMissing,
|
|
testConfig,
|
|
} from '../helpers/index.ts';
|
|
|
|
const FIXTURE_DIR = path.join(
|
|
path.dirname(path.fromFileUrl(import.meta.url)),
|
|
'../fixtures/oci'
|
|
);
|
|
|
|
describe('OCI E2E: Full lifecycle', () => {
|
|
let testUserId: string;
|
|
let testOrgName: string;
|
|
let apiToken: string;
|
|
let registryHost: string;
|
|
let shouldSkip = false;
|
|
|
|
beforeAll(async () => {
|
|
// Check if docker is available
|
|
shouldSkip = await skipIfMissing('docker');
|
|
if (shouldSkip) return;
|
|
|
|
await setupTestDb();
|
|
const url = new URL(testConfig.registry.url);
|
|
registryHost = url.host;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
if (!shouldSkip) {
|
|
await teardownTestDb();
|
|
}
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
if (shouldSkip) return;
|
|
|
|
await cleanupTestDb();
|
|
|
|
// Create test user and org
|
|
const { user } = await createTestUser({ status: 'active' });
|
|
testUserId = user.id;
|
|
|
|
const { organization } = await createOrgWithOwner(testUserId, { name: 'oci-test' });
|
|
testOrgName = organization.name;
|
|
|
|
// Create repository for OCI images
|
|
await createTestRepository({
|
|
organizationId: organization.id,
|
|
createdById: testUserId,
|
|
name: 'images',
|
|
protocol: 'oci',
|
|
});
|
|
|
|
// Create API token with OCI permissions
|
|
const { rawToken } = await createTestApiToken({
|
|
userId: testUserId,
|
|
name: 'oci-push-token',
|
|
protocols: ['oci'],
|
|
scopes: [{ protocol: 'oci', actions: ['read', 'write', 'delete'] }],
|
|
});
|
|
apiToken = rawToken;
|
|
});
|
|
|
|
it('should build and push image', async function () {
|
|
if (shouldSkip) {
|
|
console.log('Skipping: docker not available');
|
|
return;
|
|
}
|
|
|
|
const imageName = `${registryHost}/v2/${testOrgName}/demo:1.0.0`;
|
|
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.simple');
|
|
|
|
try {
|
|
// Build image
|
|
const buildResult = await clients.docker.build(dockerfile, imageName, FIXTURE_DIR);
|
|
assertEquals(buildResult.success, true, `docker build failed: ${buildResult.stderr}`);
|
|
|
|
// Login to registry
|
|
const loginResult = await clients.docker.login(registryHost, 'token', apiToken);
|
|
assertEquals(loginResult.success, true, `docker login failed: ${loginResult.stderr}`);
|
|
|
|
// Push image
|
|
const pushResult = await clients.docker.push(imageName);
|
|
assertEquals(pushResult.success, true, `docker push failed: ${pushResult.stderr}`);
|
|
} finally {
|
|
// Cleanup local image
|
|
await clients.docker.rmi(imageName, true);
|
|
}
|
|
});
|
|
|
|
it('should pull image', async function () {
|
|
if (shouldSkip) {
|
|
console.log('Skipping: docker not available');
|
|
return;
|
|
}
|
|
|
|
const imageName = `${registryHost}/v2/${testOrgName}/demo:1.0.0`;
|
|
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.simple');
|
|
|
|
try {
|
|
// Build and push first
|
|
await clients.docker.build(dockerfile, imageName, FIXTURE_DIR);
|
|
await clients.docker.login(registryHost, 'token', apiToken);
|
|
await clients.docker.push(imageName);
|
|
|
|
// Remove local image
|
|
await clients.docker.rmi(imageName, true);
|
|
|
|
// Pull from registry
|
|
const pullResult = await clients.docker.pull(imageName);
|
|
assertEquals(pullResult.success, true, `docker pull failed: ${pullResult.stderr}`);
|
|
} finally {
|
|
await clients.docker.rmi(imageName, true);
|
|
}
|
|
});
|
|
|
|
it('should handle multi-layer images', async function () {
|
|
if (shouldSkip) {
|
|
console.log('Skipping: docker not available');
|
|
return;
|
|
}
|
|
|
|
const imageName = `${registryHost}/v2/${testOrgName}/multi:1.0.0`;
|
|
const dockerfile = path.join(FIXTURE_DIR, 'Dockerfile.multi-layer');
|
|
|
|
try {
|
|
// Build multi-stage image
|
|
const buildResult = await clients.docker.build(dockerfile, imageName, FIXTURE_DIR);
|
|
assertEquals(buildResult.success, true, `docker build failed: ${buildResult.stderr}`);
|
|
|
|
// Login and push
|
|
await clients.docker.login(registryHost, 'token', apiToken);
|
|
const pushResult = await clients.docker.push(imageName);
|
|
assertEquals(pushResult.success, true, `docker push failed: ${pushResult.stderr}`);
|
|
} finally {
|
|
await clients.docker.rmi(imageName, true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('OCI E2E: Tags and versions', () => {
|
|
let shouldSkip = false;
|
|
|
|
beforeAll(async () => {
|
|
shouldSkip = await skipIfMissing('docker');
|
|
});
|
|
|
|
it('should handle multiple tags for same image', async function () {
|
|
if (shouldSkip) {
|
|
console.log('Skipping: docker not available');
|
|
return;
|
|
}
|
|
|
|
// Verify tag handling logic
|
|
const tags = ['1.0.0', '1.0', '1', 'latest'];
|
|
for (const tag of tags) {
|
|
assertEquals(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(tag), true);
|
|
}
|
|
});
|
|
|
|
it('should handle SHA256 digests', async function () {
|
|
if (shouldSkip) {
|
|
console.log('Skipping: docker not available');
|
|
return;
|
|
}
|
|
|
|
// Verify digest format
|
|
const digest = 'sha256:' + 'a'.repeat(64);
|
|
assertEquals(digest.startsWith('sha256:'), true);
|
|
assertEquals(digest.length, 71);
|
|
});
|
|
});
|