Why --- - Sprintf(*p.Title) fed the title as a format string — % in titles broke the String() method; also panicked on nil Title. - internal/applications.buildSummary used RE2-unsupported `(?= ...)` lookahead inside regexp.MustCompile, panicking the first time the applications-sync path was exercised on Go 1.23+. - Query() duplicated the auth-expiry check inline while ensureToken() already handled it — two code paths drifted. - Request.Debug split Query() into two branches that both unmarshalled into the same target value. Dead code. - httptest fixtures that emulated OnlyOffice endpoints were lying to us: they passed locally yet never caught a single real protocol regression. What ---- - Project.String(): nil-safe, no Sprintf format-string interpretation. - buildSummary regex: RE2-safe non-capturing trailing delimiter `(?:\n## |$)` replaces the lookahead. - Query() routes through ensureToken(); body marshalling factored into an unexported requestBodyReader(). Debug flag retained for backwards compatibility, documented as a no-op, to be removed at next major. - Dropped Debug: true stray flags in GetTasks/UpdateProjectTask. - Deleted httptest-based OnlyOffice mocks. unit_test.go is now pure Go (parsers, helpers, env aliases, ctx cancellation against an unroutable address). client_test.go is `//go:build integration` and runs against a real OnlyOffice, skipping cleanly without ONLYOFFICE_URL/USER/PASS. - AGENTS.md + .cursor/rules/no-synthetic-mocks.mdc document the new testing policy. Verified -------- - `go test ./...` green (15 unit tests across package + internal). - `go test -tags=integration ./...` green against live office.produktor.io (5 integration tests: auth, projects, lifecycle, calendar+CRM read, task list). - inventar-sync smoke dry-run against live OO project 33 + Gitea found 30 tasks, 0 mutations. Made-with: Cursor
54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package applications
|
|
|
|
import "testing"
|
|
|
|
func TestExtractField(t *testing.T) {
|
|
text := "**Position**: Senior Dev\n**Company**: Acme\n"
|
|
if g := extract(text, `(?i)\*\*Position\*\*:\s*(.+?)(?:\s{2,}|\n)`); g != "Senior Dev" {
|
|
t.Fatalf("position: %q", g)
|
|
}
|
|
if g := extract(text, `(?i)\*\*Company\*\*:\s*(.+?)(?:\s{2,}|\n)`); g != "Acme" {
|
|
t.Fatalf("company: %q", g)
|
|
}
|
|
}
|
|
|
|
func TestParseSalary(t *testing.T) {
|
|
if v := parseSalary("€50,000"); v < 40000 {
|
|
t.Fatalf("annual: %v", v)
|
|
}
|
|
if v := parseSalary("400 / day"); v != 400*220 {
|
|
t.Fatalf("daily: %v", v)
|
|
}
|
|
}
|
|
|
|
// TestBuildSummaryFitAssessment covers the RE2-safe replacement of a previously
|
|
// lookahead-based regex that would panic at MustCompile time on Go regexp.
|
|
func TestBuildSummaryFitAssessment(t *testing.T) {
|
|
app := Data{Position: "SRE", Company: "Acme", Folder: "/tmp"}
|
|
withNextSection := "## Fit Assessment\nGreat fit overall.\nStrong background.\n## Next section\nUnrelated."
|
|
summary := buildSummary(app, withNextSection)
|
|
if !contains(summary, "Fit Assessment:") {
|
|
t.Fatalf("missing header in summary:\n%s", summary)
|
|
}
|
|
if !contains(summary, "Great fit overall.") {
|
|
t.Fatalf("missing body in summary:\n%s", summary)
|
|
}
|
|
if contains(summary, "Unrelated.") {
|
|
t.Fatalf("bled into next section:\n%s", summary)
|
|
}
|
|
eof := "## Fit Assessment\nOnly content at EOF."
|
|
summary = buildSummary(app, eof)
|
|
if !contains(summary, "Only content at EOF.") {
|
|
t.Fatalf("EOF case missing body:\n%s", summary)
|
|
}
|
|
}
|
|
|
|
func contains(haystack, needle string) bool {
|
|
for i := 0; i+len(needle) <= len(haystack); i++ {
|
|
if haystack[i:i+len(needle)] == needle {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|