Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions internal/exporter/html.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package exporter

import (
"encoding/json"
"fmt"
"html"
"html/template"
Expand All @@ -18,7 +17,6 @@ type htmlData struct {
Format string
Iterations []htmlIteration
Summary htmlSummary
DataJSON template.JS
}

type htmlIteration struct {
Expand Down Expand Up @@ -117,9 +115,6 @@ func buildHTMLData(t *trace.Trace) htmlData {
}
}

jsonBytes, _ := json.Marshal(d.Iterations)
d.DataJSON = template.JS(string(jsonBytes))

return d
}

Expand Down
82 changes: 82 additions & 0 deletions internal/exporter/html_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package exporter

import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/loop-eng/looprelay/internal/trace"
)

func TestExportHTML_NoDeadDataJSON(t *testing.T) {
tr := trace.New([]trace.Iteration{
{
Number: 1,
Phase: trace.PhaseAct,
Action: trace.Action{Type: "Edit", Target: "main.go", Diff: "+package main\n"},
Tokens: trace.TokenUsage{InputTokens: 10, OutputTokens: 5},
Context: trace.ContextState{FillPct: 42, Compactions: 1},
CostUSD: 0.01,
},
{
Number: 2,
Phase: trace.PhaseVerify,
Verification: &trace.Verification{Status: "success", Output: "ok"},
Tokens: trace.TokenUsage{InputTokens: 8, OutputTokens: 2},
CostUSD: 0.005,
},
})
tr.SourceFile = "demo.ltf"
tr.SourceFormat = "ltf"
tr.SessionID = "sess-1"
tr.Agent = "claude"

out := filepath.Join(t.TempDir(), "replay.html")
if err := ExportHTML(tr, out); err != nil {
t.Fatalf("ExportHTML: %v", err)
}

raw, err := os.ReadFile(out)
if err != nil {
t.Fatalf("read output: %v", err)
}
html := string(raw)

if !strings.Contains(html, "Iteration 1") {
t.Error("expected iteration 1 in HTML")
}
if !strings.Contains(html, "Iteration 2") {
t.Error("expected iteration 2 in HTML")
}
if !strings.Contains(html, `const total = 2`) {
t.Error("expected JS total from summary")
}
// Regression: unused DataJSON must not be reintroduced into the page.
if strings.Contains(html, "DataJSON") {
t.Error("HTML must not reference unused DataJSON")
}
// Marshal of iterations used to inject a raw JSON blob into the page; ensure gone.
if strings.Contains(html, `"ActionType"`) || strings.Contains(html, `"PhaseClass"`) {
t.Error("HTML must not embed marshaled htmlIteration JSON (former DataJSON)")
}
}

func TestBuildHTMLData_OmitsDataJSONComputation(t *testing.T) {
tr := trace.New([]trace.Iteration{
{Number: 1, Phase: trace.PhasePlan, CostUSD: 0.02},
})
tr.SourceFile = "x.ltf"
tr.SourceFormat = "ltf"

d := buildHTMLData(tr)
if len(d.Iterations) != 1 {
t.Fatalf("expected 1 iteration, got %d", len(d.Iterations))
}
if d.Iterations[0].Phase != "plan" {
t.Errorf("expected plan phase, got %q", d.Iterations[0].Phase)
}
if d.Summary.TotalIterations != 1 {
t.Errorf("expected summary total 1, got %d", d.Summary.TotalIterations)
}
}