summaryrefslogtreecommitdiff
path: root/backend/api-test.ts
diff options
context:
space:
mode:
authoraura <nw@moneybot.cc>2026-02-17 22:39:42 +0100
committeraura <nw@moneybot.cc>2026-02-17 22:39:42 +0100
commit636b0323075225c584b62719ed51e75521bb7ffb (patch)
tree61b02271b6d0695a4beffc23fb6eb062a7da22c3 /backend/api-test.ts
push source
Diffstat (limited to 'backend/api-test.ts')
-rw-r--r--backend/api-test.ts446
1 files changed, 446 insertions, 0 deletions
diff --git a/backend/api-test.ts b/backend/api-test.ts
new file mode 100644
index 0000000..3a6344b
--- /dev/null
+++ b/backend/api-test.ts
@@ -0,0 +1,446 @@
+const BASE_URL = "https://api.axonbox.net";
+const LOGIN_EMAIL = "support@axonbox.net";
+
+import { readFile } from 'fs/promises';
+const Imap = require( 'imap' );
+
+const messages = [ {
+ role: "user",
+ content: "can you write 3 sorting algorithms for arrays of strings in c?",
+ timestamp: new Date( ).toISOString( )
+ }, {
+ role: "user",
+ content: "can you modify that to be for an array of floats?",
+ timestamp: new Date( ).toISOString( )
+ }, {
+ role: "user",
+ content: "what are the fastest sorting algorithms?",
+ timestamp: new Date( ).toISOString( )
+ }
+];
+
+function getUrl( endpoint_name: string ): string { return `${ BASE_URL }${ endpoint_name }`; }
+function getRandomIndex( length: number ): number { return Math.floor( Math.random( ) * length ); }
+async function logErrorResponse( res: Response ): Promise<boolean> {
+ console.error( await res.json( ) );
+ return false;
+}
+
+async function apiCall( endpoint: string, method: string, payload: object = { } ) {
+ return await fetch( getUrl( endpoint ), {
+ method,
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify( payload )
+ }
+ );
+}
+
+async function searchAndFetchNewEmail( mail: any ): Promise<string> {
+ return new Promise( ( resolve, reject ) => {
+ let retries = 4;
+
+ function attemptSearch( ) {
+ mail.search( [ 'UNSEEN' ], ( err, results ) => {
+ if ( err )
+ return reject( err );
+
+ if ( !results.length ) {
+ if ( retries > 0 ) {
+ --retries;
+ console.log( `No unseen emails yet. Retrying in 25 seconds ( ${retries} retries left )...` );
+ setTimeout( attemptSearch, 25000 );
+ } else {
+ mail.end( );
+ return reject( new Error( 'no unread emails after 100 seconds' ) );
+ }
+ return;
+ }
+
+ const latest_email = results[ results.length - 1 ];
+ const f = mail.fetch( latest_email, { bodies: "" } );
+ let body = "";
+
+ f.on( "message", msg => {
+ msg.on( "body", ( stream, info ) => {
+ stream.on( "data", ( chunk ) => {
+ body += chunk.toString( 'utf8' );
+ });
+ });
+
+ msg.on( 'end', ( ) => { } );
+ });
+
+ f.on( 'end', ( ) => {
+ mail.end( );
+ resolve( body );
+ });
+
+ f.on( 'error', ( fetcherr ) => {
+ mail.end( );
+ reject( fetcherr );
+ });
+ });
+ }
+
+ attemptSearch( );
+ });
+}
+
+async function getNewEmailBody( mail: any ): Promise<string> {
+ return new Promise( ( resolve, reject ) => {
+ const timeout = setTimeout( ( ) => {
+ mail.end( );
+ reject( new Error( 'Timeout waiting for email connection' ) );
+ }, 180000 );
+
+ mail.once( "ready", ( ) => {
+ mail.openBox( "INBOX", false, ( err, mailbox ) => {
+ if ( err ) {
+ clearTimeout( timeout );
+ mail.end( );
+ return reject( err );
+ }
+
+ mail.on( "mail", async ( ) => {
+ try {
+ let emailBody = await searchAndFetchNewEmail( mail );
+ clearTimeout( timeout );
+ resolve( emailBody );
+ } catch( e ) {
+ clearTimeout( timeout );
+ mail.end( );
+ reject( e );
+ }
+ });
+ });
+ });
+
+ mail.once( "error", ( err ) => {
+ clearTimeout( timeout );
+ reject( err );
+ });
+
+ mail.connect( );
+ });
+}
+
+async function getSiteToken( ) {
+ const password: string = ( await readFile( './data/mail_password.txt', 'utf8' ) ).trim( );
+
+ console.log( `\n\nsigning into email with ${ LOGIN_EMAIL }:${ password }\n\n` );
+
+ let mail = new Imap({
+ user: LOGIN_EMAIL, password,
+ host: "imappro.zoho.com",
+ port: 993,
+ tls: true
+ });
+
+ let email_body: string = "";
+ try {
+ const result = await getNewEmailBody( mail );
+
+ if ( typeof result !== "string" )
+ throw new Error( "invalid email body" );
+
+ email_body = result;
+ } catch ( err ) {
+ console.error( "failed to fetch email: ", err );
+ return null;
+ }
+
+ const login_token: string = email_body.split( "?token=" )[ 1 ];
+ if ( !login_token ) {
+ console.log( `failed to find token in ${ email_body }` );
+ return null;
+ }
+
+ const login_res = await fetch( getUrl( `/login?token=${ login_token }` ), { method: "GET" } );
+
+ if ( !login_res.ok ) {
+ console.error( "login request failed" );
+ await logErrorResponse( login_res );
+ return null;
+ }
+
+ const login_data = await login_res.json( );
+ return login_data.session;
+}
+
+const delay = ( ms: number ) => new Promise( r => setTimeout( r, ms ) );
+
+const endpoints = {
+ "/get-tokens": async ( token: string ) => await apiCall( "/get-tokens", "POST", { token } ),
+ "/create-token": async ( token: string ) => await apiCall( "/create-token", "POST", { token } ),
+ "/delete-token": async ( token: string, id: number ) => await apiCall( "/delete-token", "POST", { token, id } ),
+ "/delete-tokens": async ( token: string ) => await apiCall( "/delete-tokens", "POST", { token } ),
+ "/send-login-link": async ( ) => await apiCall( "/send-login-link", "POST", { email: `${ LOGIN_EMAIL }` } ),
+ "/invalidate-session": async ( token: string ) => await apiCall( "/invalidate-session", "POST", { token } ),
+ "/invalidate-all-sessions": async ( token: string ) => await apiCall( "/invalidate-all-sessions", "POST", { token } ),
+ "/settings": async ( token: string ) => await apiCall( "/settings", "POST", { token } ),
+ "/get-notes": async ( token: string ) => await apiCall( "/get-notes", "POST", { token } ),
+ "/getalldata": async ( token: string ) => await apiCall( "/getalldata", "POST", { token } ),
+ "/delete-note": async ( token: string, noteId: string ) => await apiCall( "/delete-note", "POST", { token, noteId } ),
+ "/delete-notes": async ( token: string ) => await apiCall( "/delete-notes", "POST", { token } ),
+ "/models": async ( ) => await apiCall( "/models", "POST" ),
+ "/get-chat": async ( token: string, chatId: string ) => await apiCall( "/get-chat", "POST", { token, chatId } ),
+ "/create-chat": async ( token: string ) => await apiCall( "/create-chat", "POST", { token } ),
+ "/generate": async ( token: string, model: string, prompt: string = "write fibonacci in c99 please" ) => await apiCall( "/generate", "POST", { token, model, prompt, suffix: "" } ),
+ "/delete-chat": async ( token: string, chatId: string ) => await apiCall( "/delete-chat", "POST", { token, chatId } ),
+ "/update-settings": async ( token: string, free: boolean, models: any[ ] ) => {
+ const available_models = models.filter( ( m ) => m.free === ( free ? 1 : 0 ) );
+
+ return await apiCall( "/update-settings", "POST", {
+ token, prefs: {
+ nickname: ( Math.floor( Math.random( ) * 90000000 ) + 10000000 ).toString( ),
+ prompt_data: {
+ system: "explain everything to me like i am " + getRandomIndex( 100 ).toString( ) + " years old.",
+ },
+ site_prefs: {
+ model: available_models[ getRandomIndex( available_models.length ) ].name,
+ },
+ },
+ });
+ },
+ "/chat": async ( token: string, chatfile: string, system: string, model: string, message: any ) =>
+ await apiCall( "/chat", "POST", {
+ token, model, chatfile, system,
+ options: { seed: 123, temperature: 0.5 },
+ messages: [ message ]
+ })
+};
+
+async function test( ): Promise<boolean> {
+ let login_link_res = await endpoints[ "/send-login-link" ]( );
+ if ( !login_link_res.ok )
+ return await logErrorResponse( login_link_res );
+ else
+ console.log( `sent login url to: '${ LOGIN_EMAIL }'!` );
+
+ let site_token = await getSiteToken( ) || "";
+ if ( !site_token ) {
+ console.log( "failed to get site tokens", site_token );
+ return false;
+ }
+
+ let settings_res = await endpoints[ "/settings" ]( site_token );
+ if ( !settings_res.ok )
+ return await logErrorResponse( settings_res );
+
+ const models_res = await endpoints[ "/models" ]( );
+ if ( !models_res.ok )
+ return await logErrorResponse( models_res );
+ const models = await models_res.json( );
+ console.log( "\ngot models: " );
+ models.models.forEach( m => console.log( m.name ) );
+ console.log( "\n" );
+
+ let settings = await settings_res.json( );
+ console.log( settings );
+ console.log( "\n" );
+
+ const is_free_tier: boolean = settings.userprefs.plan.endTime <= Date.now( );
+ const update_settings_res = await endpoints[ "/update-settings" ]( site_token, is_free_tier, models.models );
+ if ( !update_settings_res.ok )
+ return await logErrorResponse( update_settings_res );
+
+ settings_res = await endpoints[ "/settings" ]( site_token );
+ if ( !settings_res.ok )
+ return await logErrorResponse( settings_res );
+ settings = await settings_res.json( );
+ console.log( "updated settings: " );
+ console.log( settings );
+ console.log( "\n" );
+
+ let get_tokens_res = await endpoints[ "/get-tokens" ]( site_token );
+ if ( !get_tokens_res.ok )
+ return await logErrorResponse( get_tokens_res );
+ console.log( "tokens:", await get_tokens_res.json( ) );
+
+ let create_token_res = await endpoints[ "/create-token" ]( site_token );
+ if ( !create_token_res.ok )
+ return await logErrorResponse( create_token_res );
+ let create_token_res_json = await create_token_res.json( );
+ let new_token = create_token_res_json.token;
+ console.log( "\ncreated new token: " + new_token + '\n' );
+
+ get_tokens_res = await endpoints[ "/get-tokens" ]( site_token );
+ if ( !get_tokens_res.ok )
+ return await logErrorResponse( get_tokens_res );
+ let tokens_res_json = await get_tokens_res.json( );
+ console.log( "tokens:", tokens_res_json );
+
+ const generate_res = await endpoints[ "/generate" ]( new_token, settings.userprefs.site_prefs.model );
+ if ( !generate_res.ok )
+ return await logErrorResponse( generate_res );
+ console.log( "generated:", await generate_res.text( ) );
+
+ const create_chat_res = await endpoints[ "/create-chat" ]( site_token );
+ if ( !create_chat_res.ok )
+ return await logErrorResponse( create_chat_res );
+ const create_chat_res_json = await create_chat_res.json( );
+ const chat_id = create_chat_res_json.chatId;
+
+ // TODO : fix this so messages are sent back to model properly
+ // WARNING : this will break in the future when backend behaves as expected, managing message storage by itself
+ for ( let idx = 0; idx < messages.length; ++idx ) {
+ const chat_res = await endpoints[ "/chat" ](
+ new_token, chat_id,
+ settings.userprefs.prompt_data.system,
+ settings.userprefs.site_prefs.model, {
+ role: messages[ idx ].role,
+ content: messages[ idx ].content,
+ timestamp: new Date( ).toISOString( )
+ }
+ );
+
+ if ( !chat_res.ok )
+ return await logErrorResponse( chat_res );
+
+ const chat_res_text = await chat_res.text( );
+ const chunks = chat_res_text.split( "\n" );
+ const last = chunks[ chunks.length - 2 ];
+
+ console.log( "chat response: ", last );
+ await delay( 1000 );
+ }
+
+ const get_chat_res = await endpoints[ "/get-chat" ]( site_token, chat_id );
+ if ( !get_chat_res.ok )
+ return await logErrorResponse( get_chat_res );
+ const get_chat_res_json = await get_chat_res.json( );
+ console.log( `\nchat id: ${ chat_id }` );
+ console.log( `chat name: ${ get_chat_res_json.name }` );
+ console.log( `chat contents: ${ await get_chat_res_json.contents }` );
+
+ const delete_chat_res = await endpoints[ "/delete-chat" ]( site_token, chat_id );
+ if ( !delete_chat_res.ok )
+ return await logErrorResponse( delete_chat_res );
+ console.log( `\nchat ${ chat_id } deleted` );
+
+ let get_notes_res = await endpoints[ "/get-notes" ]( site_token );
+ if ( !get_notes_res.ok )
+ return await logErrorResponse( get_notes_res );
+ let get_notes_res_json = await get_notes_res.json( );
+ console.log( "found notes:", get_notes_res_json );
+
+ if ( get_notes_res_json.notes.length > 1 ) {
+ const random_note = get_notes_res_json.notes[ getRandomIndex( get_notes_res_json.notes.length ) ];
+ const delete_note_res = await endpoints[ "/delete-note" ]( site_token, random_note.id );
+ if ( !delete_note_res.ok )
+ return await logErrorResponse( delete_note_res );
+ console.log( `deleted note[ ${ random_note.id } ]: ${ random_note.content }` );
+
+ get_notes_res = await endpoints[ "/get-notes" ]( site_token );
+ if ( !get_notes_res.ok )
+ return await logErrorResponse( get_notes_res );
+ get_notes_res_json = await get_notes_res.json( );
+ console.log( "found notes:", get_notes_res_json );
+
+ if ( get_notes_res_json.notes.length > 2 ) {
+ const delete_notes_res = await endpoints[ "/delete-notes" ]( site_token );
+ if ( !delete_notes_res.ok )
+ return await logErrorResponse( delete_notes_res );
+ console.log( "deleted all notes:", await delete_notes_res.json( ) );
+ }
+
+ get_notes_res = await endpoints[ "/get-notes" ]( site_token );
+ if ( !get_notes_res.ok )
+ return await logErrorResponse( get_notes_res );
+ get_notes_res_json = await get_notes_res.json( );
+ console.log( "found notes:", get_notes_res_json );
+ } else
+ console.log( "0 ( zero ) notes to delete, skipping step" );
+
+ const token_idx = getRandomIndex( tokens_res_json.tokens.length );
+ if ( !!tokens_res_json.tokens.length ) {
+ const delete_token_res = await endpoints[ "/delete-token" ]( site_token, token_idx );
+ if ( !delete_token_res.ok )
+ return await logErrorResponse( delete_token_res );
+ console.log( "deleted token: ", await delete_token_res.json( ) );
+
+ try {
+ const chat_res = await endpoints[ "/chat" ](
+ new_token, chat_id,
+ settings.userprefs.prompt_data.system,
+ settings.userprefs.site_prefs.model,
+ messages[ 0 ]
+ );
+
+ if ( chat_res.ok )
+ return await logErrorResponse( chat_res );
+ } catch ( error ) {
+ console.log( "chat with deleted token failed as expected" );
+ }
+
+ get_tokens_res = await endpoints[ "/get-tokens" ]( site_token );
+ if ( !get_tokens_res.ok )
+ return await logErrorResponse( get_tokens_res );
+ console.log( "remaining tokens:", await get_tokens_res.json( ) );
+ } else
+ console.log( `failed to find token ${ new_token }` );
+
+ if ( tokens_res_json.tokens.length > 1 ) {
+ const delete_tokens_res = await endpoints[ "/delete-tokens" ]( site_token );
+ if ( !delete_tokens_res.ok )
+ return await logErrorResponse( delete_tokens_res );
+ console.log( "deleted all tokens: ", await delete_tokens_res.json( ) );
+
+ get_tokens_res = await endpoints[ "/get-tokens" ]( site_token );
+ if ( !get_tokens_res.ok )
+ return await logErrorResponse( get_tokens_res );
+ console.log( "remaining tokens:", await get_tokens_res.json( ) );
+ } else
+ console.log( `'${ tokens_res_json.tokens.length - 1 }' tokens, not enough to delete` );
+
+ const getalldata_res = await endpoints[ "/getalldata" ]( site_token );
+ if ( !getalldata_res.ok )
+ return await logErrorResponse( getalldata_res );
+ console.log( `getalldata returned: ${ getalldata_res.headers.get( 'Content-Length' ) } bytes of data` );
+
+ create_token_res = await endpoints[ "/create-token" ]( site_token );
+ if ( !create_token_res.ok )
+ return await logErrorResponse( create_token_res );
+ create_token_res_json = await create_token_res.json( );
+ new_token = create_token_res_json.token;
+ console.log( "\ncreated new token: " + new_token + '\n' );
+
+ const invalidate_session_res = await endpoints[ "/invalidate-session" ]( site_token );
+ if ( !invalidate_session_res.ok )
+ return await logErrorResponse( invalidate_session_res );
+ console.log( "invalidated session: ", await invalidate_session_res.json( ) );
+
+ settings_res = await endpoints[ "/settings" ]( site_token );
+ if ( settings_res.ok )
+ return await logErrorResponse( settings_res );
+
+ login_link_res = await endpoints[ "/send-login-link" ]( );
+ if ( !login_link_res.ok )
+ return await logErrorResponse( login_link_res );
+ else
+ console.log( `sent login url to: '${ LOGIN_EMAIL }'!` );
+
+ site_token = await getSiteToken( ) || "";
+ if ( !site_token ) {
+ console.log( "failed to get post-login token", site_token );
+ return false;
+ }
+
+ const invalidate_all_sessions_res = await endpoints[ "/invalidate-all-sessions" ]( site_token );
+ if ( !invalidate_all_sessions_res.ok )
+ return await logErrorResponse( invalidate_all_sessions_res );
+ console.log( "invalidated all sessions: ", await invalidate_all_sessions_res.json( ) );
+
+ settings_res = await endpoints[ "/settings" ]( "site_token" );
+ if ( settings_res.ok )
+ return await logErrorResponse( settings_res );
+
+ return true;
+}
+
+( async function( ) {
+ if ( !await test( ) )
+ console.error( "tests failed\n" );
+ else
+ console.log( "tests passed\n" );
+} ) ( ); \ No newline at end of file