autonomous.contact/tests/helpers.ts

159 lines
4 KiB
TypeScript

import { SERVER, SERVER_OPTIONS } from 'jsr:@andyburke/serverus/server';
import { convert_to_words } from 'jsr:@andyburke/lurid/word_bytes';
import { API_CLIENT } from '../utils/api.ts';
import { Cookie, getSetCookies } from '@std/http/cookie';
import { generateTotp } from '@stdext/crypto/totp';
const TLDs: string[] = [
'com',
'org',
'net',
'edu',
'gov',
'nexus',
'shop',
'unreasonablylongtldname'
];
const random_byte_buffer: Uint8Array = new Uint8Array(3);
export function random_email_address(): string {
crypto.getRandomValues(random_byte_buffer);
const name = convert_to_words(random_byte_buffer).join('-');
crypto.getRandomValues(random_byte_buffer);
const domain = convert_to_words(random_byte_buffer).join('-');
const tld = TLDs[Math.floor(Math.random() * TLDs.length)];
return `${name}@${domain}.${tld}`;
}
export function random_username(): string {
crypto.getRandomValues(random_byte_buffer);
return convert_to_words(random_byte_buffer).join('-');
}
const BASE_PORT: number = 50_000;
const MAX_PORT_OFFSET: number = 10_000;
let current_port_offset = 0;
function get_next_free_port(): number {
let free_port: number | undefined = undefined;
let attempts = 0;
do {
const port_to_try: number = BASE_PORT + (current_port_offset++ % MAX_PORT_OFFSET);
try {
Deno.listen({ port: port_to_try }).close();
free_port = port_to_try;
} catch (error) {
if (!(error instanceof Deno.errors.AddrInUse)) {
throw error;
}
}
++attempts;
if (attempts % MAX_PORT_OFFSET === 0) {
console.warn(`Tried all ports at least once while trying to locate a free one, something wrong?`);
}
} while (!free_port);
return free_port;
}
/**
* Interface defining the configuration for an ephemeral server
* @property {string} hostname - hostname bound to, default: 'localhost'
* @property {number} port - port bound to, default: next free port
* @property {SERVER} server - server instance
*/
export interface EPHEMERAL_SERVER {
hostname: string;
port: number;
server: SERVER;
}
/**
* Gets an ephemeral Serverus SERVER on an unused port.
*
* @param options Optional SERVER_OPTIONS
* @returns A LISTEN_SERVER_SETUP object with information and a reference to the server
*/
export async function get_ephemeral_listen_server(options?: SERVER_OPTIONS): Promise<EPHEMERAL_SERVER> {
const server_options = {
...{
hostname: 'localhost',
port: get_next_free_port()
},
...(options ?? {})
};
const server = new SERVER(server_options);
const ephemeral_server: EPHEMERAL_SERVER = {
hostname: server_options.hostname,
port: server_options.port,
server: await server.start()
};
return ephemeral_server;
}
type NEW_USER_INFO = {
username: string;
password: string;
};
export async function get_new_user(client: API_CLIENT, user_info?: Record<string, any>): Promise<Record<string, any>> {
const info: Record<string, any> = {
username: random_username(),
password: `${random_username()} ! ${random_username()}`,
...user_info
};
await client.fetch('/users', {
method: 'POST',
json: info
});
const cookies: Cookie[] = [];
const auth_response: any = await client.fetch('/auth', {
method: 'POST',
json: info,
done: (response) => {
cookies.push(...getSetCookies(response.headers));
}
});
info.user = auth_response.user;
info.session = auth_response.session;
cookies.push({
name: 'totp',
value: await generateTotp(info.session?.secret),
maxAge: 30,
expires: Date.now() + 30_000,
path: '/'
});
info.headers_for_get = new Headers();
for (const cookie of cookies) {
info.headers_for_get.append(`x-${cookie.name}`, cookie.value);
}
info.headers_for_get.append('cookie', cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; '));
return info;
}
export async function set_user_permissions(client: API_CLIENT, user: any, session: any, permissions: string[]): Promise<any> {
return await client.fetch(`/users/${user.id}`, {
method: 'PUT',
headers: {
'x-session_id': session.id,
'x-totp': await generateTotp(session.secret),
'x-test-override': 'true'
},
json: {
permissions
}
});
}