Address #129 review: one date path, ordered years, trimmed dates
Build image / build-and-push (push) Successful in 8s
Build image / build-and-push (push) Successful in 8s
- Every dated tool argument now goes through day() → parseDay, so a prose date on remove_planting / remove_plantings / clear_object (and place, fill, journal) is refused with the same message as update_planting's. - parseDay's trimmed value is what gets stored, not the raw argument. - list_years re-sorts after adding the gardener's year instead of prepending it: newest first holds when their year is the oldest. - ClearSeedLot matches its JSON tag; the label-clearing branch says why nil. - The prompt says the notes are facts to plan with, not instructions. Left as is: update_garden's read-then-overlay merge. UpdateGarden is whole-row by design (the REST PATCH sends every field too), and a service GardenPatch would duplicate gardenFromInput's validation for one caller. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -3,6 +3,7 @@ package agent
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -721,17 +722,20 @@ func TestToolsFromTheLiveSweep(t *testing.T) {
|
||||
// (a bare API caller) still dates everything: the service's UTC today.
|
||||
func TestToolsDefaultToTheServiceDayWithoutOne(t *testing.T) {
|
||||
a := &adapter{today: ""}
|
||||
if d := a.day(""); d != nil {
|
||||
t.Errorf("no day at all → %q, want nil (the service default)", *d)
|
||||
if d, err := a.day(""); d != nil || err != nil {
|
||||
t.Errorf("no day at all → %v, %v; want nil (the service default)", d, err)
|
||||
}
|
||||
if d := a.day(" 2026-01-02 "); d == nil || *d != "2026-01-02" {
|
||||
t.Errorf("an explicit day → %v, want it trimmed", d)
|
||||
if d, err := a.day(" 2026-01-02 "); err != nil || d == nil || *d != "2026-01-02" {
|
||||
t.Errorf("an explicit day → %v, %v; want it trimmed", d, err)
|
||||
}
|
||||
if d, err := a.day("Tuesday"); err == nil || !strings.Contains(err.Error(), "YYYY-MM-DD") {
|
||||
t.Errorf("a prose day → %v, %v; want a refusal naming the format", d, err)
|
||||
}
|
||||
a.today = "2026-08-22"
|
||||
if d := a.day(""); d == nil || *d != "2026-08-22" {
|
||||
if d, _ := a.day(""); d == nil || *d != "2026-08-22" {
|
||||
t.Errorf("the gardener's day → %v, want 2026-08-22", d)
|
||||
}
|
||||
if d := a.day("2026-05-20"); d == nil || *d != "2026-05-20" {
|
||||
if d, _ := a.day("2026-05-20"); d == nil || *d != "2026-05-20" {
|
||||
t.Errorf("an explicit day beats the default: %v", d)
|
||||
}
|
||||
}
|
||||
@@ -822,7 +826,20 @@ func TestRecordKeepingTools(t *testing.T) {
|
||||
t.Error("count and clearCount together were accepted")
|
||||
}
|
||||
|
||||
// --- remove_planting on the day the gardener said, not today.
|
||||
// A padded date is stored clean, not refused downstream with a bare error.
|
||||
mustCall("update_planting", map[string]any{"plantingId": plop.ID, "version": plop.Version, "plantedAt": " 2026-05-20 "}, &plop)
|
||||
if plop.PlantedAt == nil || *plop.PlantedAt != "2026-05-20" {
|
||||
t.Errorf("padded plantedAt stored as %v", plop.PlantedAt)
|
||||
}
|
||||
|
||||
// --- remove_planting on the day the gardener said, not today; a prose day
|
||||
// is refused the same way every dated tool refuses one.
|
||||
for _, tool := range []string{"remove_planting", "remove_plantings", "clear_object"} {
|
||||
args := map[string]any{"plantingId": plop.ID, "version": plop.Version, "objectId": bed.ID, "plantId": beet.ID, "removedAt": "Aug 1"}
|
||||
if r := call(tool, args); !r.IsError || !strings.Contains(r.Content, "YYYY-MM-DD") {
|
||||
t.Errorf("%s with a prose removedAt = %q, want a refusal naming the format", tool, r.Content)
|
||||
}
|
||||
}
|
||||
mustCall("remove_planting", map[string]any{"plantingId": plop.ID, "version": plop.Version, "removedAt": "2026-08-01"}, &plop)
|
||||
if plop.RemovedAt == nil || *plop.RemovedAt != "2026-08-01" {
|
||||
t.Errorf("removedAt = %v, want the harvest date 2026-08-01", plop.RemovedAt)
|
||||
@@ -834,6 +851,15 @@ func TestRecordKeepingTools(t *testing.T) {
|
||||
if len(years.Years) == 0 || years.Years[0] != 2026 {
|
||||
t.Errorf("years = %v, want 2026 first", years.Years)
|
||||
}
|
||||
// A gardener whose local year is behind the data's gets it listed, in
|
||||
// order — newest first holds even when theirs is the oldest.
|
||||
raw := NewToolbox(svc, owner, "2024-12-31").Execute(ctx, llm.ToolCall{ID: "3", Name: "list_years", Arguments: mustJSON(t, map[string]any{"gardenId": g.ID})})
|
||||
if err := json.Unmarshal([]byte(raw.Content), &years); err != nil || raw.IsError {
|
||||
t.Fatalf("list_years for 2024: %v %s", err, raw.Content)
|
||||
}
|
||||
if !sort.SliceIsSorted(years.Years, func(i, j int) bool { return years.Years[i] > years.Years[j] }) || years.Years[len(years.Years)-1] != 2024 {
|
||||
t.Errorf("years for a 2024 gardener = %v, want newest first with 2024 last", years.Years)
|
||||
}
|
||||
mustCall("describe_garden", map[string]any{"gardenId": g.ID}, &desc)
|
||||
if len(desc.Objects[0].Plantings) != 0 {
|
||||
t.Errorf("the live describe still lists the pulled beet: %+v", desc.Objects[0].Plantings)
|
||||
|
||||
Reference in New Issue
Block a user