From 4f5f829c8167df0ca67b8f414c2c8c3227bc411d Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Sun, 6 Sep 2026 22:46:40 +0200 Subject: [PATCH] test: add shared api client tests and remove dead backend code --- docs/todo.md | 3 +- packages/backend/src/csp.rs | 16 ------- packages/backend/src/main.rs | 1 - packages/backend/src/store.rs | 7 --- packages/shared/src/api.test.ts | 75 +++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 25 deletions(-) delete mode 100644 packages/backend/src/csp.rs create mode 100644 packages/shared/src/api.test.ts diff --git a/docs/todo.md b/docs/todo.md index bab79fc..94ae130 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -2,4 +2,5 @@ - also update readmes in other languages - use catalog install for common deps (typescript, vite, tsdown, etc. ) please suggest. -- move formatting, linting, type checking and git hooks to vite-plus (uses oxlint, oxfmt, vitest and git hook dispatcher) \ No newline at end of file +- move formatting, linting, type checking and git hooks to vite-plus (uses oxlint, oxfmt, vitest and git hook dispatcher) +- re-add CSP (Content-Security-Policy) into axum router ( (was in csp.rs, removed as unused) diff --git a/packages/backend/src/csp.rs b/packages/backend/src/csp.rs deleted file mode 100644 index e21ad69..0000000 --- a/packages/backend/src/csp.rs +++ /dev/null @@ -1,16 +0,0 @@ -use axum::{body::Body, extract::Request, http::HeaderValue, middleware::Next, response::Response}; - -const CUSTOM_HEADER_NAME: &str = "Content-Security-Policy"; -const CUSTOM_HEADER_VALUE: &str = "default-src 'self'; script-src 'report-sample' 'self'; style-src 'report-sample' 'self'; object-src 'none'; base-uri 'self'; connect-src 'self' data:; font-src 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; worker-src 'none';"; - -lazy_static! { - static ref HEADER_VALUE: HeaderValue = HeaderValue::from_static(CUSTOM_HEADER_VALUE); -} - -pub async fn add_csp_header(request: Request, next: Next) -> Response { - let mut response = next.run(request).await; - response - .headers_mut() - .append(CUSTOM_HEADER_NAME, HEADER_VALUE.clone()); - response -} diff --git a/packages/backend/src/main.rs b/packages/backend/src/main.rs index f51a59f..ff27313 100644 --- a/packages/backend/src/main.rs +++ b/packages/backend/src/main.rs @@ -15,7 +15,6 @@ use tower_http::{ extern crate lazy_static; mod config; -mod csp; mod health; mod note; mod status; diff --git a/packages/backend/src/store.rs b/packages/backend/src/store.rs index 7dbe098..c9a9979 100644 --- a/packages/backend/src/store.rs +++ b/packages/backend/src/store.rs @@ -68,13 +68,6 @@ pub fn get_data(id: &str) -> Result>, &'static str> { Ok(data) } -pub fn has_views(id: &str) -> Result { - let key = prefixed(id); - let mut c = conn()?; - let has: bool = c.hexists::<_, _, bool>(&key, "views").map_err(|_| "Cache error")?; - Ok(has) -} - pub fn decrement_views(id: &str) -> Result { let key = prefixed(id); let mut c = conn()?; diff --git a/packages/shared/src/api.test.ts b/packages/shared/src/api.test.ts new file mode 100644 index 0000000..9925c3a --- /dev/null +++ b/packages/shared/src/api.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it, vi } from 'vitest' +import { encode, decode } from '@msgpack/msgpack' +import { setServer, getServer, create, info, get, status } from './api' + +const server = 'http://example.test' +const created = encode({ id: 'abc123' }) +const metaOut = encode({ meta: { views: 3, extra: Buffer.from('040506','hex') } }) +const dataOut = encode({ meta: { views: 0 },data: Buffer.from('090909','hex') }) + +function mockFetch(body: Uint8Array) { + return vi.fn().mockResolvedValue({ + ok: true, + status: 200, + arrayBuffer: async () => body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength), + json: async () => ({}), + }) +} + +function copyBuffer(buf: Uint8Array) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) +} + +describe('api client', () => { + it('setServer trims trailing slashes', () => { + setServer('http://x.test///') + expect(getServer()).toBe('http://x.test') + }) + + it('create POSTs msgpack note and returns id', async () => { + setServer(server) + const fetchMock = mockFetch(created) + vi.stubGlobal('fetch', fetchMock) + const note = { meta: { views: 5 },data: Buffer.from('010203','hex') } + const result = await create(note) + const url = fetchMock.mock.calls[0]![0]! + const init = fetchMock.mock.calls[0]![1]! + expect(url).toBe(server + '/api/v3/notes') + expect(init.method).toBe('POST') + expect(init.headers).toEqual({ 'content-type': 'application/msgpack' }) + const sent = decode(new Uint8Array(copyBuffer(init.body))) as { meta?: { views?: number } } + expect(sent?.meta?.views).toBe(5) + expect(result).toEqual({ id: 'abc123' }) + vi.unstubAllGlobals() + }) + + it('info GETs meta', async () => { + setServer(server) + const fetchMock = mockFetch(metaOut) + vi.stubGlobal('fetch', fetchMock) + const result = await info('id1') + expect(result?.views).toBe(3) + vi.unstubAllGlobals() + }) + + it('get DELETEs and parses data', async () => { + setServer(server) + const fetchMock = mockFetch(dataOut) + vi.stubGlobal('fetch', fetchMock) + const result = await get('id2') + expect(result?.meta?.views).toBe(0) + const init = fetchMock.mock.calls[0]![1]! + expect(init.method).toBe('DELETE') + vi.unstubAllGlobals() + }) + + it('status GETs JSON config', async () => { + setServer(server) + const fetchMock = mockFetch(new Uint8Array()) + vi.stubGlobal('fetch', fetchMock) + await status() + const url = fetchMock.mock.calls[0]![0]! + expect(url).toBe(server + '/api/v3/status') + vi.unstubAllGlobals() + }) +})