// ingestion/internal/api/handler_test.go package api_test import ( "bytes" "encoding/json" "log/slog" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "github.com/mathiasbq/hyperguild/ingestion/internal/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func setup(t *testing.T) (string, *api.Handler) { t.Helper() dir := t.TempDir() require.NoError(t, os.MkdirAll(filepath.Join(dir, "wiki", "concepts"), 0o755)) require.NoError(t, os.MkdirAll(filepath.Join(dir, "raw"), 0o755)) require.NoError(t, os.WriteFile( filepath.Join(dir, "wiki", "concepts", "tdd.md"), []byte("---\ntitle: TDD\ndomain: software\n---\n\nTest-driven development is a discipline.\n"), 0o644, )) logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) return dir, api.NewHandler(dir, logger) } func TestQuery_ReturnsResults(t *testing.T) { _, h := setup(t) body, _ := json.Marshal(map[string]any{"query": "test driven", "limit": 5}) req := httptest.NewRequest(http.MethodPost, "/query", bytes.NewReader(body)) rec := httptest.NewRecorder() h.Query(rec, req) assert.Equal(t, http.StatusOK, rec.Code) var resp map[string]any require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) results := resp["results"].([]any) assert.NotEmpty(t, results) } func TestWrite_CreatesRawFile(t *testing.T) { dir, h := setup(t) body, _ := json.Marshal(map[string]any{ "content": "# Test note\n\nSome content.", "filename": "test-note.md", }) req := httptest.NewRequest(http.MethodPost, "/write", bytes.NewReader(body)) rec := httptest.NewRecorder() h.Write(rec, req) assert.Equal(t, http.StatusOK, rec.Code) var resp map[string]string require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) assert.NotEmpty(t, resp["path"]) written := filepath.Join(dir, "raw", "test-note.md") content, err := os.ReadFile(written) require.NoError(t, err) assert.Contains(t, string(content), "Some content.") } func TestQuery_RequiresQuery(t *testing.T) { _, h := setup(t) body, _ := json.Marshal(map[string]any{"limit": 5}) req := httptest.NewRequest(http.MethodPost, "/query", bytes.NewReader(body)) rec := httptest.NewRecorder() h.Query(rec, req) assert.Equal(t, http.StatusBadRequest, rec.Code) } func TestWrite_IncludesFrontmatterWhenTypeProvided(t *testing.T) { dir, h := setup(t) body, _ := json.Marshal(map[string]any{ "content": "Some learning.", "filename": "typed-note.md", "type": "concept", "domain": "software", }) req := httptest.NewRequest(http.MethodPost, "/write", bytes.NewReader(body)) rec := httptest.NewRecorder() h.Write(rec, req) assert.Equal(t, http.StatusOK, rec.Code) content, err := os.ReadFile(filepath.Join(dir, "raw", "typed-note.md")) require.NoError(t, err) assert.Contains(t, string(content), "type: concept") assert.Contains(t, string(content), "domain: software") assert.Contains(t, string(content), "Some learning.") } func TestWrite_GeneratesFilenameIfAbsent(t *testing.T) { dir, h := setup(t) body, _ := json.Marshal(map[string]any{"content": "auto name"}) req := httptest.NewRequest(http.MethodPost, "/write", bytes.NewReader(body)) rec := httptest.NewRecorder() h.Write(rec, req) assert.Equal(t, http.StatusOK, rec.Code) entries, _ := os.ReadDir(filepath.Join(dir, "raw")) assert.Len(t, entries, 1) assert.True(t, strings.HasSuffix(entries[0].Name(), ".md")) }