Add unit tests for models and services
- 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.
This commit is contained in:
208
test/helpers/subprocess.helper.ts
Normal file
208
test/helpers/subprocess.helper.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Subprocess helper - utilities for running protocol clients in tests
|
||||
*/
|
||||
|
||||
export interface ICommandResult {
|
||||
success: boolean;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
code: number;
|
||||
signal?: Deno.Signal;
|
||||
}
|
||||
|
||||
export interface ICommandOptions {
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
timeout?: number;
|
||||
stdin?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command and return the result
|
||||
*/
|
||||
export async function runCommand(
|
||||
cmd: string[],
|
||||
options: ICommandOptions = {}
|
||||
): Promise<ICommandResult> {
|
||||
const { cwd, env, timeout = 60000, stdin } = options;
|
||||
|
||||
const command = new Deno.Command(cmd[0], {
|
||||
args: cmd.slice(1),
|
||||
cwd,
|
||||
env: { ...Deno.env.toObject(), ...env },
|
||||
stdin: stdin ? 'piped' : 'null',
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const child = command.spawn();
|
||||
|
||||
if (stdin && child.stdin) {
|
||||
const writer = child.stdin.getWriter();
|
||||
await writer.write(new TextEncoder().encode(stdin));
|
||||
await writer.close();
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
try {
|
||||
child.kill('SIGTERM');
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
const output = await child.output();
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
return {
|
||||
success: output.success,
|
||||
stdout: new TextDecoder().decode(output.stdout),
|
||||
stderr: new TextDecoder().decode(output.stderr),
|
||||
code: output.code,
|
||||
signal: output.signal ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a command is available
|
||||
*/
|
||||
export async function commandExists(cmd: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await runCommand(['which', cmd], { timeout: 5000 });
|
||||
return result.success;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocol client wrappers
|
||||
*/
|
||||
export const clients = {
|
||||
npm: {
|
||||
check: () => commandExists('npm'),
|
||||
publish: (dir: string, registry: string, token: string) =>
|
||||
runCommand(['npm', 'publish', '--registry', registry], {
|
||||
cwd: dir,
|
||||
env: { NPM_TOKEN: token, npm_config__authToken: token },
|
||||
}),
|
||||
install: (pkg: string, registry: string, dir: string) =>
|
||||
runCommand(['npm', 'install', pkg, '--registry', registry], { cwd: dir }),
|
||||
unpublish: (pkg: string, registry: string, token: string) =>
|
||||
runCommand(['npm', 'unpublish', pkg, '--registry', registry, '--force'], {
|
||||
env: { NPM_TOKEN: token, npm_config__authToken: token },
|
||||
}),
|
||||
pack: (dir: string) => runCommand(['npm', 'pack'], { cwd: dir }),
|
||||
},
|
||||
|
||||
docker: {
|
||||
check: () => commandExists('docker'),
|
||||
build: (dockerfile: string, tag: string, context: string) =>
|
||||
runCommand(['docker', 'build', '-f', dockerfile, '-t', tag, context]),
|
||||
push: (image: string) => runCommand(['docker', 'push', image]),
|
||||
pull: (image: string) => runCommand(['docker', 'pull', image]),
|
||||
rmi: (image: string, force = false) =>
|
||||
runCommand(['docker', 'rmi', ...(force ? ['-f'] : []), image]),
|
||||
login: (registry: string, username: string, password: string) =>
|
||||
runCommand(['docker', 'login', registry, '-u', username, '--password-stdin'], {
|
||||
stdin: password,
|
||||
}),
|
||||
tag: (source: string, target: string) => runCommand(['docker', 'tag', source, target]),
|
||||
},
|
||||
|
||||
cargo: {
|
||||
check: () => commandExists('cargo'),
|
||||
package: (dir: string) => runCommand(['cargo', 'package', '--allow-dirty'], { cwd: dir }),
|
||||
publish: (dir: string, registry: string, token: string) =>
|
||||
runCommand(
|
||||
['cargo', 'publish', '--registry', 'stack-test', '--token', token, '--allow-dirty'],
|
||||
{ cwd: dir }
|
||||
),
|
||||
yank: (crate: string, version: string, token: string) =>
|
||||
runCommand([
|
||||
'cargo',
|
||||
'yank',
|
||||
crate,
|
||||
'--version',
|
||||
version,
|
||||
'--registry',
|
||||
'stack-test',
|
||||
'--token',
|
||||
token,
|
||||
]),
|
||||
},
|
||||
|
||||
pip: {
|
||||
check: () => commandExists('pip'),
|
||||
build: (dir: string) => runCommand(['python', '-m', 'build', dir]),
|
||||
upload: (dist: string, repository: string, token: string) =>
|
||||
runCommand([
|
||||
'python',
|
||||
'-m',
|
||||
'twine',
|
||||
'upload',
|
||||
'--repository-url',
|
||||
repository,
|
||||
'-u',
|
||||
'__token__',
|
||||
'-p',
|
||||
token,
|
||||
`${dist}/*`,
|
||||
]),
|
||||
install: (pkg: string, indexUrl: string) =>
|
||||
runCommand(['pip', 'install', pkg, '--index-url', indexUrl]),
|
||||
},
|
||||
|
||||
composer: {
|
||||
check: () => commandExists('composer'),
|
||||
install: (pkg: string, repository: string, dir: string) =>
|
||||
runCommand(
|
||||
[
|
||||
'composer',
|
||||
'require',
|
||||
pkg,
|
||||
'--repository',
|
||||
JSON.stringify({ type: 'composer', url: repository }),
|
||||
],
|
||||
{ cwd: dir }
|
||||
),
|
||||
},
|
||||
|
||||
gem: {
|
||||
check: () => commandExists('gem'),
|
||||
build: (gemspec: string, dir: string) => runCommand(['gem', 'build', gemspec], { cwd: dir }),
|
||||
push: (gemFile: string, host: string, key: string) =>
|
||||
runCommand(['gem', 'push', gemFile, '--host', host, '--key', key]),
|
||||
install: (gemName: string, source: string) =>
|
||||
runCommand(['gem', 'install', gemName, '--source', source]),
|
||||
yank: (gemName: string, version: string, host: string, key: string) =>
|
||||
runCommand(['gem', 'yank', gemName, '-v', version, '--host', host, '--key', key]),
|
||||
},
|
||||
|
||||
maven: {
|
||||
check: () => commandExists('mvn'),
|
||||
deploy: (dir: string, repositoryUrl: string, username: string, password: string) =>
|
||||
runCommand(
|
||||
[
|
||||
'mvn',
|
||||
'deploy',
|
||||
`-DaltDeploymentRepository=stack-test::default::${repositoryUrl}`,
|
||||
`-Dusername=${username}`,
|
||||
`-Dpassword=${password}`,
|
||||
],
|
||||
{ cwd: dir }
|
||||
),
|
||||
package: (dir: string) => runCommand(['mvn', 'package', '-DskipTests'], { cwd: dir }),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Skip test if command is not available
|
||||
*/
|
||||
export async function skipIfMissing(cmd: string): Promise<boolean> {
|
||||
const exists = await commandExists(cmd);
|
||||
if (!exists) {
|
||||
console.warn(`[Skip] ${cmd} not available`);
|
||||
}
|
||||
return !exists;
|
||||
}
|
||||
Reference in New Issue
Block a user