Files
harness-sync/internal/adapters/pi/pi_test.go
T
lukaszraczylo 0865c405fb feat(pi): emit model context and output limits when set
The pi adapter writes contextWindow and maxTokens to models.json only
when the profile sets them. Pi keeps its own defaults for every other
model. Tests cover both cases.
2026-10-02 05:11:46 +01:00

150 lines
5.1 KiB
Go

package pi
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/lukaszraczylo/harness-sync/internal/adapter"
"github.com/lukaszraczylo/harness-sync/internal/canonical"
)
func TestPiRender(t *testing.T) {
home := t.TempDir()
ad := New(WithHome(home))
b := &canonical.Bundle{
Root: "/canon",
Profile: canonical.Profile{
Gateway: canonical.Gateway{URL: "https://gw", Token: "dummy", DefaultModel: "sonnet"},
Models: []canonical.Model{{ID: "claude-sonnet-4-6", Alias: "Sonnet"}},
},
Instructions: canonical.Instructions{Global: "# global"},
}
fs, err := ad.Render(b)
require.NoError(t, err)
seen := map[string]adapter.File{}
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
skillsDest := filepath.Join(home, ".pi", "agent", "skills")
assert.Equal(t, adapter.SymlinkDir, seen[skillsDest].Kind)
assert.Equal(t, "/canon/skills", seen[skillsDest].SymlinkTarget)
agentsDest := filepath.Join(home, ".pi", "agent", "AGENTS.md")
assert.Equal(t, adapter.RenderedFile, seen[agentsDest].Kind)
assert.Contains(t, string(seen[agentsDest].Content), "# global")
settingsDest := filepath.Join(home, ".pi", "agent", "settings.json")
var settings map[string]any
require.NoError(t, json.Unmarshal(seen[settingsDest].Content, &settings))
assert.Equal(t, "hs-gw", settings["defaultProvider"])
assert.Equal(t, "sonnet", settings["defaultModel"])
modelsDest := filepath.Join(home, ".pi", "agent", "models.json")
var models map[string]any
require.NoError(t, json.Unmarshal(seen[modelsDest].Content, &models))
providers := models["providers"].(map[string]any)
gw := providers["hs-gw"].(map[string]any)
assert.Equal(t, "https://gw", gw["baseUrl"])
assert.Equal(t, "openai-completions", gw["api"])
assert.Equal(t, "dummy", gw["apiKey"])
modelList := gw["models"].([]any)
first := modelList[0].(map[string]any)
assert.Equal(t, "claude-sonnet-4-6", first["id"])
assert.Equal(t, "Sonnet", first["name"])
// A model with no explicit limits must carry none, so pi keeps applying
// its own defaults rather than inheriting canonical's.
assert.NotContains(t, first, "contextWindow")
assert.NotContains(t, first, "maxTokens")
}
func TestPiRenderEmitsExplicitModelLimits(t *testing.T) {
home := t.TempDir()
ad := New(WithHome(home))
b := &canonical.Bundle{
Profile: canonical.Profile{
Gateway: canonical.Gateway{URL: "https://gw", Token: "dummy"},
Models: []canonical.Model{
{ID: "local/current-model", Alias: "Local", Context: 1048576, Output: 32768},
{ID: "bare/model"},
},
},
}
fs, err := ad.Render(b)
require.NoError(t, err)
seen := map[string]adapter.File{}
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
var models map[string]any
require.NoError(t, json.Unmarshal(seen[filepath.Join(home, ".pi", "agent", "models.json")].Content, &models))
list := models["providers"].(map[string]any)["hs-gw"].(map[string]any)["models"].([]any)
withLimits := list[0].(map[string]any)
assert.InDelta(t, float64(1048576), withLimits["contextWindow"], 0)
assert.InDelta(t, float64(32768), withLimits["maxTokens"], 0)
withoutLimits := list[1].(map[string]any)
assert.NotContains(t, withoutLimits, "contextWindow")
assert.NotContains(t, withoutLimits, "maxTokens")
}
func TestPiRenderMergesExistingSettings(t *testing.T) {
home := t.TempDir()
base := filepath.Join(home, ".pi", "agent")
require.NoError(t, os.MkdirAll(base, 0o750))
existing := map[string]any{
"theme": "light",
"defaultThinkingLevel": "high",
}
body, _ := json.Marshal(existing)
require.NoError(t, os.WriteFile(filepath.Join(base, "settings.json"), body, 0o600))
ad := New(WithHome(home))
b := &canonical.Bundle{Profile: canonical.Profile{Gateway: canonical.Gateway{URL: "https://gw", DefaultModel: "sonnet"}}}
fs, err := ad.Render(b)
require.NoError(t, err)
seen := map[string]adapter.File{}
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
var parsed map[string]any
require.NoError(t, json.Unmarshal(seen[filepath.Join(base, "settings.json")].Content, &parsed))
assert.Equal(t, "light", parsed["theme"])
assert.Equal(t, "high", parsed["defaultThinkingLevel"])
assert.Equal(t, "hs-gw", parsed["defaultProvider"])
assert.Equal(t, "sonnet", parsed["defaultModel"])
}
func TestPiRenderWithoutGatewaySkipsProviderFiles(t *testing.T) {
home := t.TempDir()
ad := New(WithHome(home))
b := &canonical.Bundle{Root: "/canon"}
fs, err := ad.Render(b)
require.NoError(t, err)
seen := map[string]adapter.File{}
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
assert.Contains(t, seen, filepath.Join(home, ".pi", "agent", "skills"))
assert.NotContains(t, seen, filepath.Join(home, ".pi", "agent", "settings.json"))
assert.NotContains(t, seen, filepath.Join(home, ".pi", "agent", "models.json"))
}
func TestPiImport(t *testing.T) {
home := t.TempDir()
base := filepath.Join(home, ".pi", "agent")
require.NoError(t, os.MkdirAll(base, 0o750))
require.NoError(t, os.WriteFile(filepath.Join(base, "AGENTS.md"), []byte("# global"), 0o600))
ad := New(WithHome(home))
res, err := ad.Import(home)
require.NoError(t, err)
assert.Equal(t, "# global", res.Instructions)
}