refactor(ax): round 4 AX sweep — test naming, variable names, usage examples
- Rename all test functions in metrics_test.go to TestFilename_Function_{Good,Bad,Ugly} convention (TestMetrics_Record_*, TestMetrics_ReadEvents_*, etc.)
- Rename cmd_test.go tests to TestCmd_ParseDuration_{Good,Bad}
- Rename bench test TestMetricsBench_RecordAndRead_10K_Good and replace inline HOME setup with withTempHome helper
- Replace short vars sev/unit/value with severityLower/unitChar/numericPart in filterBySeverity and parseDuration
- Add usage-example comments to embed/avg/qualityLabel/truncate/modelAvailable in embed-bench
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
4256cefffa
commit
035a5b7e53
6 changed files with 42 additions and 36 deletions
|
|
@ -189,19 +189,9 @@ func BenchmarkMetricsRecordAndQuery(b *testing.B) {
|
|||
|
||||
// --- Unit tests for metrics at scale ---
|
||||
|
||||
// TestMetricsRecordAndRead_10K_Good writes 10K events and reads them back.
|
||||
func TestMetricsRecordAndRead_10K_Good(t *testing.T) {
|
||||
// Override HOME to temp dir
|
||||
origHome := os.Getenv("HOME")
|
||||
tmpHome := t.TempDir()
|
||||
metricsPath := filepath.Join(tmpHome, ".core", "ai", "metrics")
|
||||
if err := coreio.Local.EnsureDir(metricsPath); err != nil {
|
||||
t.Fatalf("Failed to create metrics dir: %v", err)
|
||||
}
|
||||
os.Setenv("HOME", tmpHome)
|
||||
t.Cleanup(func() {
|
||||
os.Setenv("HOME", origHome)
|
||||
})
|
||||
// TestMetricsBench_RecordAndRead_10K_Good writes 10K events and reads them back.
|
||||
func TestMetricsBench_RecordAndRead_10K_Good(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
now := time.Now()
|
||||
const n = 10_000
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ func withTempHome(t *testing.T) {
|
|||
|
||||
// --- Record ---
|
||||
|
||||
func TestRecord_Good(t *testing.T) {
|
||||
func TestMetrics_Record_Good(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
ev := Event{
|
||||
|
|
@ -52,7 +52,7 @@ func TestRecord_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecord_Good_AutoTimestamp(t *testing.T) {
|
||||
func TestMetrics_Record_Good_AutoTimestamp(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
before := time.Now()
|
||||
|
|
@ -75,7 +75,7 @@ func TestRecord_Good_AutoTimestamp(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecord_Good_PresetTimestamp(t *testing.T) {
|
||||
func TestMetrics_Record_Good_PresetTimestamp(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
fixed := time.Date(2026, 3, 15, 10, 0, 0, 0, time.UTC)
|
||||
|
|
@ -98,7 +98,7 @@ func TestRecord_Good_PresetTimestamp(t *testing.T) {
|
|||
|
||||
// --- ReadEvents ---
|
||||
|
||||
func TestReadEvents_Good_Empty(t *testing.T) {
|
||||
func TestMetrics_ReadEvents_Good_Empty(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
events, err := ReadEvents(time.Now().Add(-24 * time.Hour))
|
||||
|
|
@ -110,7 +110,7 @@ func TestReadEvents_Good_Empty(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestReadEvents_Good_MultiDay(t *testing.T) {
|
||||
func TestMetrics_ReadEvents_Good_MultiDay(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
now := time.Now()
|
||||
|
|
@ -138,7 +138,7 @@ func TestReadEvents_Good_MultiDay(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestReadEvents_Good_FiltersBySince(t *testing.T) {
|
||||
func TestMetrics_ReadEvents_Good_FiltersBySince(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
now := time.Now()
|
||||
|
|
@ -167,7 +167,7 @@ func TestReadEvents_Good_FiltersBySince(t *testing.T) {
|
|||
|
||||
// --- readMetricsFile ---
|
||||
|
||||
func TestReadMetricsFile_Good_MalformedLines(t *testing.T) {
|
||||
func TestMetrics_ReadMetricsFile_Good_MalformedLines(t *testing.T) {
|
||||
withTempHome(t)
|
||||
|
||||
dir, err := metricsDir()
|
||||
|
|
@ -195,7 +195,7 @@ not-json
|
|||
}
|
||||
}
|
||||
|
||||
func TestReadMetricsFile_Good_NonExistent(t *testing.T) {
|
||||
func TestMetrics_ReadMetricsFile_Good_NonExistent(t *testing.T) {
|
||||
events, err := readMetricsFile("/tmp/nonexistent-metrics-file.jsonl", time.Time{})
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error for missing file, got: %v", err)
|
||||
|
|
@ -207,7 +207,7 @@ func TestReadMetricsFile_Good_NonExistent(t *testing.T) {
|
|||
|
||||
// --- metricsFilePath ---
|
||||
|
||||
func TestMetricsFilePath_Good(t *testing.T) {
|
||||
func TestMetrics_MetricsFilePath_Good(t *testing.T) {
|
||||
ts := time.Date(2026, 3, 17, 14, 30, 0, 0, time.UTC)
|
||||
got := metricsFilePath("/base", ts)
|
||||
want := "/base/2026-03-17.jsonl"
|
||||
|
|
@ -218,7 +218,7 @@ func TestMetricsFilePath_Good(t *testing.T) {
|
|||
|
||||
// --- Summary ---
|
||||
|
||||
func TestSummary_Good_Empty(t *testing.T) {
|
||||
func TestMetrics_Summary_Good_Empty(t *testing.T) {
|
||||
s := Summary(nil)
|
||||
total, ok := s["total"].(int)
|
||||
if !ok || total != 0 {
|
||||
|
|
@ -226,7 +226,7 @@ func TestSummary_Good_Empty(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSummary_Good(t *testing.T) {
|
||||
func TestMetrics_Summary_Good(t *testing.T) {
|
||||
events := []Event{
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-1"},
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-2"},
|
||||
|
|
@ -252,14 +252,14 @@ func TestSummary_Good(t *testing.T) {
|
|||
|
||||
// --- sortedMap ---
|
||||
|
||||
func TestSortedMap_Good_Empty(t *testing.T) {
|
||||
func TestMetrics_SortedMap_Good_Empty(t *testing.T) {
|
||||
result := sortedMap(map[string]int{})
|
||||
if len(result) != 0 {
|
||||
t.Errorf("expected empty slice, got %d entries", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortedMap_Good_Ordering(t *testing.T) {
|
||||
func TestMetrics_SortedMap_Good_Ordering(t *testing.T) {
|
||||
m := map[string]int{"a": 1, "b": 3, "c": 2}
|
||||
result := sortedMap(m)
|
||||
if len(result) != 3 {
|
||||
|
|
|
|||
|
|
@ -233,6 +233,9 @@ type embedResponse struct {
|
|||
Embedding []float64 `json:"embedding"`
|
||||
}
|
||||
|
||||
// embed requests a vector embedding from Ollama for the given model and text.
|
||||
//
|
||||
// vec, _ := embed("nomic-embed-text", "how does the brain recall work?")
|
||||
func embed(model, text string) ([]float64, error) {
|
||||
body, _ := json.Marshal(embedRequest{Model: model, Prompt: text})
|
||||
resp, err := httpClient.Post(*ollamaURL+"/api/embeddings", "application/json", bytes.NewReader(body))
|
||||
|
|
@ -253,6 +256,9 @@ func embed(model, text string) ([]float64, error) {
|
|||
return result.Embedding, nil
|
||||
}
|
||||
|
||||
// modelAvailable returns true if the model is listed in Ollama's local tag registry.
|
||||
//
|
||||
// modelAvailable("nomic-embed-text") // → true if pulled
|
||||
func modelAvailable(model string) bool {
|
||||
resp, err := httpClient.Get(*ollamaURL + "/api/tags")
|
||||
if err != nil {
|
||||
|
|
@ -292,6 +298,9 @@ func cosine(a, b []float64) float64 {
|
|||
return dot / denom
|
||||
}
|
||||
|
||||
// avg returns the arithmetic mean of a float64 slice, or 0 for an empty slice.
|
||||
//
|
||||
// avg([]float64{0.8, 0.6, 0.9}) // → 0.7666...
|
||||
func avg(vals []float64) float64 {
|
||||
if len(vals) == 0 {
|
||||
return 0
|
||||
|
|
@ -303,6 +312,10 @@ func avg(vals []float64) float64 {
|
|||
return sum / float64(len(vals))
|
||||
}
|
||||
|
||||
// qualityLabel maps a cosine separation gap to a human-readable quality band.
|
||||
//
|
||||
// qualityLabel(0.18) // → "(excellent)"
|
||||
// qualityLabel(0.03) // → "(poor)"
|
||||
func qualityLabel(gap float64) string {
|
||||
switch {
|
||||
case gap > 0.15:
|
||||
|
|
@ -316,6 +329,9 @@ func qualityLabel(gap float64) string {
|
|||
}
|
||||
}
|
||||
|
||||
// truncate shortens s to at most n characters, appending "..." if truncated.
|
||||
//
|
||||
// truncate("How does the emotional scoring work?", 20) // → "How does the emot..."
|
||||
func truncate(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
|
|
|
|||
|
|
@ -110,11 +110,11 @@ func parseDuration(s string) (time.Duration, error) {
|
|||
return 0, coreerr.E("metrics.parseDuration", fmt.Sprintf("invalid duration: %s", s), nil)
|
||||
}
|
||||
|
||||
unit := s[len(s)-1]
|
||||
value := s[:len(s)-1]
|
||||
unitChar := s[len(s)-1]
|
||||
numericPart := s[:len(s)-1]
|
||||
|
||||
var count int
|
||||
if _, err := fmt.Sscanf(value, "%d", &count); err != nil {
|
||||
if _, err := fmt.Sscanf(numericPart, "%d", &count); err != nil {
|
||||
return 0, coreerr.E("metrics.parseDuration", fmt.Sprintf("invalid duration: %s", s), nil)
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ func parseDuration(s string) (time.Duration, error) {
|
|||
return 0, coreerr.E("metrics.parseDuration", fmt.Sprintf("duration must be positive: %s", s), nil)
|
||||
}
|
||||
|
||||
switch unit {
|
||||
switch unitChar {
|
||||
case 'd':
|
||||
return time.Duration(count) * 24 * time.Hour, nil
|
||||
case 'h':
|
||||
|
|
@ -130,6 +130,6 @@ func parseDuration(s string) (time.Duration, error) {
|
|||
case 'm':
|
||||
return time.Duration(count) * time.Minute, nil
|
||||
default:
|
||||
return 0, coreerr.E("metrics.parseDuration", fmt.Sprintf("unknown unit %c in duration: %s", unit, s), nil)
|
||||
return 0, coreerr.E("metrics.parseDuration", fmt.Sprintf("unknown unit %c in duration: %s", unitChar, s), nil)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestParseDuration_Good(t *testing.T) {
|
||||
func TestCmd_ParseDuration_Good(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want time.Duration
|
||||
|
|
@ -29,7 +29,7 @@ func TestParseDuration_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseDuration_Bad(t *testing.T) {
|
||||
func TestCmd_ParseDuration_Bad(t *testing.T) {
|
||||
bad := []string{
|
||||
"", // too short
|
||||
"d", // too short
|
||||
|
|
|
|||
|
|
@ -188,9 +188,9 @@ func filterBySeverity(severity, filter string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
sev := strings.ToLower(severity)
|
||||
return slices.ContainsFunc(slices.Collect(strings.SplitSeq(strings.ToLower(filter), ",")), func(s string) bool {
|
||||
return strings.TrimSpace(s) == sev
|
||||
severityLower := strings.ToLower(severity)
|
||||
return slices.ContainsFunc(slices.Collect(strings.SplitSeq(strings.ToLower(filter), ",")), func(filterEntry string) bool {
|
||||
return strings.TrimSpace(filterEntry) == severityLower
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue