1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
4
5package mcp
6
7import (
8 "context"
9 "testing"
10
11 "git.secluded.site/cooked-mcp/internal/cooked"
12)
13
14// recipes.SAVE.3 recipes.SAVE.4 recipes.SAVE.9 tools.SAVE_RECIPE_TOOL.1
15func TestCallToolACIDRecipesSave3And4And9ImportsURLSavedRecipe(t *testing.T) {
16 backend := &fakeBackend{importRecipeURLResult: cooked.RecipeURLImport{RecipeID: "recipe-1"}}
17 server := NewServer(backend, "test")
18
19 _, output, err := server.callSaveRecipeTool(
20 context.Background(),
21 nil,
22 SaveRecipeArguments{Source: " url ", URL: " https://example.invalid/recipe "},
23 )
24 if err != nil {
25 t.Fatalf("callSaveRecipeTool() error = %v", err)
26 }
27
28 if backend.importRecipeURLCalls != 1 {
29 t.Fatalf("import calls = %d, want 1", backend.importRecipeURLCalls)
30 }
31 if backend.importURL != "https://example.invalid/recipe" {
32 t.Fatalf("backend import URL = %q, want trimmed URL", backend.importURL)
33 }
34 if output.RecipeID != "recipe-1" || output.DraftID != "" {
35 t.Fatalf("save output = %#v, want recipe_id only", output)
36 }
37 if backend.metadataCalls != 0 || backend.contentCalls != 0 {
38 t.Fatalf("draft read calls = metadata %d content %d, want none", backend.metadataCalls, backend.contentCalls)
39 }
40 if backend.saveCalls != 0 || backend.updateCalls != 0 {
41 t.Fatalf("save/update calls = %d/%d, want none", backend.saveCalls, backend.updateCalls)
42 }
43}
44
45// recipes.SAVE.3-1 tools.SAVE_RECIPE_TOOL.4
46func TestCallToolACIDRecipesSave3_1AndToolsSaveRecipeTool4RequiresURL(t *testing.T) {
47 backend := &fakeBackend{}
48 server := NewServer(backend, "test")
49
50 _, _, err := server.callSaveRecipeTool(context.Background(), nil, SaveRecipeArguments{Source: "url", URL: " "})
51 if err == nil {
52 t.Fatal("callSaveRecipeTool() error = nil, want missing URL error")
53 }
54 if backend.importRecipeURLCalls != 0 {
55 t.Fatalf("import calls = %d, want none", backend.importRecipeURLCalls)
56 }
57}
58
59// recipes.SAVE.5 recipes.SAVE.6 recipes.SAVE.12 recipes.SAVE.14 recipes.SAVE.15 recipes.SAVE.16
60func TestCallToolACIDRecipesSave5And12And14And15ReturnsURLImportDraft(t *testing.T) {
61 backend := &fakeBackend{
62 importRecipeURLResult: cooked.RecipeURLImport{DraftID: "draft-1"},
63 recipeMetadata: cooked.RecipeMetadata{Title: "Pasta"},
64 recipeContent: cooked.RecipeContent{Content: "# Pasta\n\n1. Boil pasta.", Portions: 2},
65 }
66 server := NewServer(backend, "test")
67
68 _, output, err := server.callSaveRecipeTool(
69 context.Background(),
70 nil,
71 SaveRecipeArguments{Source: "url", URL: " https://example.invalid/pasta "},
72 )
73 if err != nil {
74 t.Fatalf("callSaveRecipeTool() error = %v", err)
75 }
76
77 if backend.importRecipeURLCalls != 1 {
78 t.Fatalf("import calls = %d, want 1", backend.importRecipeURLCalls)
79 }
80 if backend.importURL != "https://example.invalid/pasta" {
81 t.Fatalf("backend import URL = %q, want trimmed URL", backend.importURL)
82 }
83 if backend.metadataRecipeID != "draft-1" || backend.contentRecipeID != "draft-1" {
84 t.Fatalf(
85 "draft read IDs = metadata %q content %q, want draft-1",
86 backend.metadataRecipeID,
87 backend.contentRecipeID,
88 )
89 }
90 if backend.metadataCalls != 1 || backend.contentCalls != 1 {
91 t.Fatalf("draft read calls = metadata %d content %d, want 1/1", backend.metadataCalls, backend.contentCalls)
92 }
93 if output.RecipeID != "" || output.DraftID != "draft-1" || output.Title != "Pasta" {
94 t.Fatalf("draft output identity = %#v, want draft-1 Pasta with no recipe ID", output)
95 }
96 if output.Markdown != "# Pasta\n\n1. Boil pasta." || output.Portions != 2 {
97 t.Fatalf("draft output content = %q/%g, want markdown/2", output.Markdown, output.Portions)
98 }
99 if backend.saveCalls != 0 || backend.updateCalls != 0 {
100 t.Fatalf("save/update calls = %d/%d, want none", backend.saveCalls, backend.updateCalls)
101 }
102}