diff --git a/backend/build.mjs b/backend/build.mjs new file mode 100644 index 0000000..0d9687c --- /dev/null +++ b/backend/build.mjs @@ -0,0 +1,36 @@ +import { readdir, readFile, writeFile, mkdir } from 'fs/promises'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname, resolve } from 'path'; + +const nodeDir = dirname(process.execPath); +const tsPath = resolve(nodeDir, '..', 'lib', 'node_modules', 'typescript', 'lib', 'typescript.js'); +const ts = await import(tsPath); + +async function getFiles(dir) { + const entries = await readdir(dir, { withFileTypes: true }); + const files = await Promise.all(entries.map(async (entry) => { + const res = path.join(dir, entry.name); + return entry.isDirectory() ? await getFiles(res) : res; + })); + return files.flat(); +} + +const srcFiles = await getFiles('src'); +const testFiles = await getFiles('test'); +const allFiles = srcFiles.concat(testFiles).filter((f) => f.endsWith('.ts')); + +for (const file of allFiles) { + const source = await readFile(file, 'utf8'); + const { outputText } = ts.transpileModule(source, { + compilerOptions: { + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.ESNext, + esModuleInterop: true, + }, + fileName: file, + }); + const outPath = path.join('dist', file.replace(/\.ts$/, '.js')); + await mkdir(path.dirname(outPath), { recursive: true }); + await writeFile(outPath, outputText); +} diff --git a/backend/package.json b/backend/package.json index 4e45b05..061ac4b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,6 +3,10 @@ "module": "index.ts", "type": "module", "private": true, + "scripts": { + "build": "node build.mjs", + "test": "npm run build && node --test \"dist/test/**/*.js\"" + }, "devDependencies": { "@types/bun": "latest", "@types/express": "^5.0.2" diff --git a/backend/src/index.ts b/backend/src/index.ts index fd7c1df..e90689b 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -26,8 +26,11 @@ app.use("/containers", containerRoutes); app.use("/chat", chatRoutes); const PORT = process.env.PORT || 4000; -app.listen(PORT, () => { - console.log(`Docker Container API running on port ${PORT}`); -}); + +if (process.env.NODE_ENV !== "test") { + app.listen(PORT, () => { + console.log(`Docker Container API running on port ${PORT}`); + }); +} export default app; diff --git a/backend/test/chat.test.ts b/backend/test/chat.test.ts new file mode 100644 index 0000000..09d2e55 --- /dev/null +++ b/backend/test/chat.test.ts @@ -0,0 +1,27 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +process.env.NODE_ENV = 'test'; +const { default: app } = await import('../src/index.js'); + +function getPort(server: any): number { + const address = server.address(); + if (typeof address === 'string' || !address) { + throw new Error('Invalid server address'); + } + return address.port; +} + +test('GET /chat/:id/messages returns session data', async (t) => { + const server = app.listen(0); + t.after(() => server.close()); + const port = getPort(server); + + const response = await fetch(`http://localhost:${port}/chat/test/messages`); + const body = await response.json(); + + assert.equal(response.status, 200); + assert.equal(body.success, true); + assert.ok(Array.isArray(body.messages)); + assert.equal(typeof body.sessionId, 'string'); +}); diff --git a/backend/test/docker.test.ts b/backend/test/docker.test.ts new file mode 100644 index 0000000..d2d563e --- /dev/null +++ b/backend/test/docker.test.ts @@ -0,0 +1,8 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { getDockerfile } from '../src/services/docker.js'; + +test('getDockerfile reads Dockerfile content', async () => { + const content = await getDockerfile(); + assert.ok(content.includes('FROM node:18-alpine')); +});