diff --git a/.gitignore b/.gitignore index 52dc9c3..0b4f4ed 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage/ .dolt/ *.db .beads-credential-key +AGY.md diff --git a/plan.md b/plan.md deleted file mode 100644 index 682b4d7..0000000 --- a/plan.md +++ /dev/null @@ -1,32 +0,0 @@ -# Feature: NoteAPI v2 — Search, Pagination, and Archiving - -## Problem Statement -The API supports basic CRUD but lacks the query features users need for real use: filtering by tag, searching content, paginating large result sets, and archiving old notes without deleting them. - -## Approach -Add four features incrementally. Each feature is independent — no ordering dependencies. All use the existing in-memory store (no database changes). Each feature must have tests before it's considered done. - -## Phases - -### Phase 1: Tag Filtering -- [ ] GET /api/notes?tag=work returns only notes with that tag -- [ ] Tests: single tag, no match, multiple tags on same note -- Integration test: `curl localhost:3000/api/notes?tag=work` - -### Phase 2: Full-Text Search -- [ ] GET /api/notes?q=meeting searches title and content (case-insensitive) -- [ ] Tests: match in title, match in content, no match, empty query returns all -- Integration test: `curl localhost:3000/api/notes?q=meeting` - -### Phase 3: Pagination -- [ ] GET /api/notes?page=1&limit=10 returns paginated results -- [ ] Response format: `{ data: [...], total: N, page: N, limit: N }` -- [ ] Default: page 1, limit 20 -- Integration test: create 25 notes, verify page 1 has 20, page 2 has 5 - -### Phase 4: Note Archiving -- [ ] Add `archived: boolean` field to Note model (default: false) -- [ ] POST /api/notes/:id/archive and /api/notes/:id/unarchive endpoints -- [ ] GET /api/notes excludes archived by default -- [ ] GET /api/notes?include_archived=true includes them -- Integration test: archive a note, verify it's hidden, unarchive, verify it's back diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..57c04b6 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,31 @@ +const args = process.argv.slice(2); + +// Check if any argument is empty or blank +if (args.some(arg => arg.trim() === '')) { + console.error("Error: Arguments cannot be empty or blank strings."); + process.exit(1); +} + +if (args.includes('--version') || args.includes('-v')) { + console.log("fleet-e2e-toy v1.0.0"); + process.exit(0); +} + +if (args.includes('--help') || args.includes('-h') || args.includes('help')) { + console.log(`Usage: fleet-e2e-toy [options] [command] + +Options: + -v, --version Show version number + -h, --help Show help + +Commands: + help Show help`); + process.exit(0); +} + +// Reject unknown commands or flags +if (args.length > 0) { + console.error(`Unknown command or flag: ${args[0]}`); + process.exit(1); +} + diff --git a/tests/cli.test.ts b/tests/cli.test.ts new file mode 100644 index 0000000..ed2f7c4 --- /dev/null +++ b/tests/cli.test.ts @@ -0,0 +1,68 @@ +import { spawnSync } from "child_process"; +import * as path from "path"; + +describe("CLI Tests", () => { + const runCLI = (args: string[]) => { + const cliPath = path.resolve(__dirname, "../src/cli.ts"); + + const result = spawnSync("node", ["-r", "ts-node/register", cliPath, ...args], { + encoding: "utf8", + }); + + return { + stdout: result.stdout || "", + stderr: result.stderr || "", + status: result.status, + }; + }; + + it("prints version with --version flag", () => { + const { stdout, status } = runCLI(["--version"]); + expect(status).toBe(0); + expect(stdout.trim()).toBe("fleet-e2e-toy v1.0.0"); + }); + + it("prints version with -v flag", () => { + const { stdout, status } = runCLI(["-v"]); + expect(status).toBe(0); + expect(stdout.trim()).toBe("fleet-e2e-toy v1.0.0"); + }); + + it("prints usage with help subcommand", () => { + const { stdout, status } = runCLI(["help"]); + expect(status).toBe(0); + expect(stdout).toContain("Usage: fleet-e2e-toy"); + expect(stdout).toContain("Options:"); + expect(stdout).toContain("Commands:"); + }); + + it("prints usage with --help flag", () => { + const { stdout, status } = runCLI(["--help"]); + expect(status).toBe(0); + expect(stdout).toContain("Usage: fleet-e2e-toy"); + }); + + it("prints usage with -h flag", () => { + const { stdout, status } = runCLI(["-h"]); + expect(status).toBe(0); + expect(stdout).toContain("Usage: fleet-e2e-toy"); + }); + + it("rejects empty string argument", () => { + const { stderr, status } = runCLI([""]); + expect(status).toBe(1); + expect(stderr.trim()).toContain("Error: Arguments cannot be empty or blank strings."); + }); + + it("rejects blank string argument", () => { + const { stderr, status } = runCLI([" "]); + expect(status).toBe(1); + expect(stderr.trim()).toContain("Error: Arguments cannot be empty or blank strings."); + }); + + it("rejects unknown command", () => { + const { stderr, status } = runCLI(["unknown_command"]); + expect(status).toBe(1); + expect(stderr.trim()).toContain("Unknown command or flag: unknown_command"); + }); +}); diff --git a/tool b/tool new file mode 100755 index 0000000..98b8ead --- /dev/null +++ b/tool @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +npx ts-node src/cli.ts "$@" diff --git a/tool.cmd b/tool.cmd new file mode 100644 index 0000000..1c72f71 --- /dev/null +++ b/tool.cmd @@ -0,0 +1,2 @@ +@echo off +npx ts-node src/cli.ts %*