import { Lacuna } from '../'; import { getLacuna } from '../__utils__/get-lacuna'; import { expectMatchesApiSchema } from '../__utils__/validate-schema'; import type { Species } from '../types/empire'; /** A valid species (its affinities total 45), for calls that never get applied. */ const SPECIES: Species = { name: 'Probe', description: 'Never actually applied to anything.', min_orbit: 3, max_orbit: 3, manufacturing_affinity: 4, deception_affinity: 4, research_affinity: 4, management_affinity: 4, farming_affinity: 4, mining_affinity: 4, science_affinity: 4, environmental_affinity: 4, political_affinity: 4, trade_affinity: 4, growth_affinity: 1, }; test('login with errors', async () => { const lacuna = new Lacuna(); const { error } = await lacuna.empire.login({ empire_name: 'non-existant-empire', password: 'incorrect-password', api_key: 'anonymous', }); expect(error?.code).toBe(1002); expect(error?.message).toBe('Empire does not exist.'); expect(error?.data).toBe('non-existant-empire'); }); test('login', async () => { const lacuna = new Lacuna(); const { result } = await lacuna.empire.login({ empire_name: process.env.LACUNA_TEST_EMPIRE_NAME, password: process.env.LACUNA_TEST_EMPIRE_PASSWORD, api_key: 'anonymous', }); expect(result?.session_id).toBeDefined(); }); test('logout', async () => { // A dedicated login (not the shared/cached session from getLacuna() - see // lib/__utils__/get-lacuna.ts) so logging out here can't kill a session // other, concurrently-running test files are still relying on. const lacuna = new Lacuna(); await lacuna.authenticate( process.env.LACUNA_TEST_EMPIRE_NAME, process.env.LACUNA_TEST_EMPIRE_PASSWORD ); const response = await lacuna.empire.logout(); expect(response?.result).toBe(1); }); test('viewBoosts', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.viewBoosts(); expect(result?.boosts.food).toBeDefined(); expect(result?.boosts.ore).toBeDefined(); expect(result?.boosts.water).toBeDefined(); expect(result?.boosts.energy).toBeDefined(); expect(result?.boosts.storage).toBeDefined(); expect(result?.boosts.spy_training).toBeDefined(); expect(result?.boosts.building).toBeDefined(); expect(result?.boosts.happiness).toBeDefined(); expectMatchesApiSchema('empire', 'view_boosts', result); }); test('getStatus', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.getStatus(); expect(result?.empire).toBeDefined(); expect(result?.server).toBeDefined(); expect(result?.empire.name).toBe(process.env.LACUNA_TEST_EMPIRE_NAME); expectMatchesApiSchema('empire', 'get_status', result); }); test('fetchCaptcha', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.fetchCaptcha(); expect(result?.guid).toBeDefined(); expect(result?.url).toBeDefined(); expectMatchesApiSchema('empire', 'fetch_captcha', result); }); test('isNameAvailable', async () => { const lacuna = new Lacuna(); const { result } = await lacuna.empire.isNameAvailable({ name: 'A Name Nobody Has Taken' }); expect(result).toBe(1); // A name that *isn't* available is an exception rather than a 0. const { error } = await lacuna.empire.isNameAvailable({ name: process.env.LACUNA_TEST_EMPIRE_NAME, }); expect(error?.code).toBe(1000); expect(error?.message).toBe('Empire name is in use by another player.'); }); test('viewProfile', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.viewProfile(); expect(result?.profile.player_name).toBeDefined(); expect(result?.profile.sitter_password).toBeDefined(); expect(result?.profile.medals).toBeDefined(); expectMatchesApiSchema('empire', 'view_profile', result); }); test('viewPublicProfile', async () => { const lacuna = await getLacuna(); const { result: status } = await lacuna.empire.getStatus(); const { result } = await lacuna.empire.viewPublicProfile({ empire_id: status!.empire.id }); expect(result?.profile.name).toBe(process.env.LACUNA_TEST_EMPIRE_NAME); expect(result?.profile.colony_count).toBeGreaterThan(0); expect(result?.profile.known_colonies.length).toBeGreaterThan(0); // Spec-vs-server divergence in README "Spec Discrepancies": public medals // carry no `public` flag (they're all public by definition, but the spec // reuses view_profile's medal schema, which requires one). expectMatchesApiSchema('empire', 'view_public_profile', result, [ { keyword: 'required', instancePath: '/profile/medals/*' }, ]); }); test('find', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.find({ name: process.env.LACUNA_TEST_EMPIRE_NAME }); expect(result?.empires).toContainEqual({ id: expect.any(Number), name: process.env.LACUNA_TEST_EMPIRE_NAME, }); expectMatchesApiSchema('empire', 'find', result); }); test('getInviteFriendUrl', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.getInviteFriendUrl(); expect(result?.referral_url).toBeDefined(); expect(result?.status.server).toBeDefined(); expectMatchesApiSchema('empire', 'get_invite_friend_url', result); }); test('setStatusMessage', async () => { const lacuna = await getLacuna(); // Set to whatever it already says, so the test doesn't change the empire. const { result: profile } = await lacuna.empire.viewProfile(); const message = profile!.profile.status_message; const { result } = await lacuna.empire.setStatusMessage({ message }); expect(result?.empire.status_message).toBe(message); expect(result?.server).toBeDefined(); // The response is the status block itself rather than the spec's `{status}` // wrapper - see README "Spec Discrepancies". expectMatchesApiSchema('empire', 'set_status_message', result, [ { keyword: 'required', instancePath: '' }, ]); }); test('viewSpeciesStats', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.viewSpeciesStats(); expect(result?.species.name).toBeDefined(); expect(result?.species.growth_affinity).toBeDefined(); expectMatchesApiSchema('empire', 'view_species_stats', result); }); test('getSpeciesTemplates', async () => { // Sessionless: the templates are a static list, and the empire that needs // them to fill in updateSpecies hasn't been founded yet. const lacuna = new Lacuna(); const { result } = await lacuna.empire.getSpeciesTemplates(); expect(result!.length).toBeGreaterThan(0); expect(result![0].name).toBeDefined(); // Every template has to be a legal updateSpecies submission: the eleven // affinities, plus a point for each orbit from min_orbit to max_orbit // inclusive, must total exactly 45. One that doesn't is one the server // would reject if a caller submitted it back unchanged. for (const template of result!) { const affinities = template.manufacturing_affinity + template.deception_affinity + template.research_affinity + template.management_affinity + template.farming_affinity + template.mining_affinity + template.science_affinity + template.environmental_affinity + template.political_affinity + template.trade_affinity + template.growth_affinity; const orbits = template.max_orbit - template.min_orbit + 1; expect(affinities + orbits).toBe(45); } expectMatchesApiSchema('empire', 'get_species_templates', result); }); test('redefineSpeciesLimits', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.redefineSpeciesLimits(); expect(result?.essentia_cost).toBeGreaterThan(0); expect([0, 1]).toContain(result?.can); expectMatchesApiSchema('empire', 'redefine_species_limits', result); }); test('viewAuthorizedSitters', async () => { const lacuna = await getLacuna(); const { result } = await lacuna.empire.viewAuthorizedSitters(); expect(Array.isArray(result?.sitters)).toBe(true); expectMatchesApiSchema('empire', 'view_authorized_sitters', result); }); // Anything that spends essentia, sends email, or can only run part-way through // empire creation is taken as far as the server's own argument checking and no // further. A 1006 ("session expired", from calling without one) or a // method-specific error proves the route is one the v2 API actually serves - // a 1002 "not recognized by the v2 API's argument map" would not. describe('methods checked without being carried out', () => { test('create', async () => { const lacuna = new Lacuna(); const { error } = await lacuna.empire.create({ name: 'A Name Nobody Has Taken', captcha_guid: 'not-a-real-guid', captcha_solution: 'not-a-real-solution', }); expect(error?.code).toBe(1014); }); test('found', async () => { const lacuna = new Lacuna(); const { error } = await lacuna.empire.found({ empire_id: 999999999, api_key: 'anonymous' }); expect(error?.code).toBe(1002); expect(error?.message).toBe('Invalid empire.'); }); test('updateSpecies', async () => { // The backend refuses this for a founded empire with a raw 1010, the same // code it uses for the RPC limit - so opt out of the retry handler that // would otherwise keep retrying it until the test times out. const lacuna = await getLacuna({ handleRpcLimit: false }); const { result: status } = await lacuna.empire.getStatus(); // Founded empires are exactly what update_species refuses to touch, so the // fixture empire can stand in for a real one without any risk to it. const { error } = await lacuna.empire.updateSpecies({ empire_id: status!.empire.id, params: SPECIES, }); expect(error?.code).toBe(1010); }); test('sendPasswordResetMessage', async () => { const lacuna = new Lacuna(); // No empire, so no recovery email goes anywhere. const { error } = await lacuna.empire.sendPasswordResetMessage({ empire_name: 'non-existant-empire', }); expect(error?.code).toBe(1002); expect(error?.message).toBe('Empire not found.'); }); test('resetPassword', async () => { const lacuna = new Lacuna(); const { error } = await lacuna.empire.resetPassword({ reset_key: 0, password1: '1234qwer', password2: '1234qwer', api_key: 'anonymous', }); expect(error?.code).toBe(1002); expect(error?.message).toBe('The key you provided is invalid. Password not reset.'); }); test('authorizeSitters', async () => { const lacuna = await getLacuna(); // An id nobody owns, so there's nothing to authorize even if it lands. const { error } = await lacuna.empire.authorizeSitters({ empires: [-99], }); if (error?.code === 1016) { console.warn('Skipping schema check: authorize_sitters requires a captcha', error); return; } expect(error?.code).toBe(1006); }); test('deauthorizeSitters', async () => { const lacuna = await getLacuna(); // An id nobody owns, so no real sitter is removed. const { result } = await lacuna.empire.deauthorizeSitters({ empires: [999999999] }); expect(Array.isArray(result?.sitters)).toBe(true); expectMatchesApiSchema('empire', 'deauthorize_sitters', result); }); });