feat: upstream generic workspace tooling (board-sync, crm audit, catalog names)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m16s
Tests / Test (Go stable) (pull_request) Successful in 1m36s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m16s
Tests / Test (Go stable) (pull_request) Successful in 1m36s
Promote the generic, reusable parts of the private oo-workspace into the public library/CLI, so oo-workspace can shrink to business glue. - board.go: Board types + (c *Client) SyncBoard — upsert project milestones/tasks by exact title from a YAML board (dry-run when apply=false). CLI: oo projects board-sync. - crm_audit.go: OpportunityAudit + (c *Client) AuditOpportunities — file/task/member counts with a generic class (ok|dup|empty|junk-title). CLI: oo crm audit [--out]. - catalog/names.go: CleanPersonNames / GuessNameFromEmail / FormatProjectTitle (ported from oo-workspace). - catalog: adopt the newer apply/match logic — name repair (never encode company in lastName), company grouping key, contact-info type normalization, preserve an already-applied oo_id. Keeps the local Address/OOProjects fields and the config-driven classifier. Business-only oo-workspace commands (crm clean/migrate, folders, search) deliberately stay private.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package onlyoffice
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseBoard(t *testing.T) {
|
||||
b, err := ParseBoard([]byte(`
|
||||
projects:
|
||||
- id: 13
|
||||
name: Example
|
||||
milestones:
|
||||
- title: "[lq] Test"
|
||||
deadline: "2026-01-15"
|
||||
key: true
|
||||
tasks:
|
||||
- title: Draft
|
||||
start: "2026-01-02"
|
||||
deadline: "2026-01-10"
|
||||
description: "…"
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(b.Projects) != 1 || b.Projects[0].ID != 13 {
|
||||
t.Fatalf("projects: %+v", b.Projects)
|
||||
}
|
||||
ms := b.Projects[0].Milestones[0]
|
||||
if ms.Title != "[lq] Test" || !ms.Key || ms.Deadline != "2026-01-15" {
|
||||
t.Fatalf("milestone: %+v", ms)
|
||||
}
|
||||
if len(ms.Tasks) != 1 || ms.Tasks[0].Title != "Draft" || ms.Tasks[0].Start != "2026-01-02" {
|
||||
t.Fatalf("task: %+v", ms.Tasks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBoardEmpty(t *testing.T) {
|
||||
if _, err := ParseBoard([]byte("projects: []")); err == nil {
|
||||
t.Fatal("expected error for empty board")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardMilestoneIDs(t *testing.T) {
|
||||
title := "[lq] Test"
|
||||
id := int64(9)
|
||||
got := boardMilestoneIDs([]*Milestone{{Title: &title, ID: &id}, nil})
|
||||
if got[title] != 9 {
|
||||
t.Fatalf("%v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardTaskTitles(t *testing.T) {
|
||||
got := boardTaskTitles([]map[string]any{{"title": "a"}, {"title": "b"}, {"nope": 1}})
|
||||
if _, ok := got["a"]; !ok {
|
||||
t.Fatal("missing a")
|
||||
}
|
||||
if _, ok := got["b"]; !ok {
|
||||
t.Fatal("missing b")
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("%v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardDay(t *testing.T) {
|
||||
if _, err := boardDay("2026-01-15"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := boardDay("15.01.2026"); err == nil {
|
||||
t.Fatal("expected error for non-ISO date")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user