You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
6.3 KiB
193 lines
6.3 KiB
package notion |
|
|
|
import ( |
|
"context" |
|
"testing" |
|
) |
|
|
|
func ptrBool(b bool) *bool { return &b } |
|
func ptrFloat(f float64) *float64 { return &f } |
|
func ptrStr(s string) *string { return &s } |
|
|
|
func TestRenderValue(t *testing.T) { |
|
c := &Client{} // network-free property types only |
|
ctx := context.Background() |
|
|
|
cases := []struct { |
|
name string |
|
prop property |
|
want string |
|
}{ |
|
{"title", property{Type: "title", Title: []richText{{PlainText: "기획 "}, {PlainText: "문서"}}}, "기획 문서"}, |
|
{"rich_text", property{Type: "rich_text", RichText: []richText{{PlainText: "메모"}}}, "메모"}, |
|
{"select", property{Type: "select", Select: &selectOption{Name: "진행중"}}, "진행중"}, |
|
{"status", property{Type: "status", Status: &selectOption{Name: "완료"}}, "완료"}, |
|
{"multi_select", property{Type: "multi_select", MultiSelect: []selectOption{{Name: "A"}, {Name: "B"}}}, "A, B"}, |
|
{"checkbox_on", property{Type: "checkbox", Checkbox: ptrBool(true)}, "✓ 체크됨"}, |
|
{"checkbox_off", property{Type: "checkbox", Checkbox: ptrBool(false)}, "✗ 해제됨"}, |
|
{"number", property{Type: "number", Number: ptrFloat(42)}, "42"}, |
|
{"url", property{Type: "url", URL: ptrStr("https://x")}, "https://x"}, |
|
{"email", property{Type: "email", Email: ptrStr("a@b.com")}, "a@b.com"}, |
|
{"empty_select", property{Type: "select"}, ""}, |
|
} |
|
for _, tc := range cases { |
|
t.Run(tc.name, func(t *testing.T) { |
|
if got := c.renderValue(ctx, tc.prop); got != tc.want { |
|
t.Errorf("renderValue = %q, want %q", got, tc.want) |
|
} |
|
}) |
|
} |
|
} |
|
|
|
func TestRenderDate(t *testing.T) { |
|
c := &Client{} |
|
ctx := context.Background() |
|
single := property{Type: "date"} |
|
single.Date = &struct { |
|
Start string `json:"start"` |
|
End string `json:"end"` |
|
}{Start: "2026-06-20"} |
|
if got := c.renderValue(ctx, single); got != "2026-06-20" { |
|
t.Errorf("single date = %q", got) |
|
} |
|
rng := property{Type: "date"} |
|
rng.Date = &struct { |
|
Start string `json:"start"` |
|
End string `json:"end"` |
|
}{Start: "2026-06-20", End: "2026-06-25"} |
|
if got := c.renderValue(ctx, rng); got != "2026-06-20 ~ 2026-06-25" { |
|
t.Errorf("range date = %q", got) |
|
} |
|
} |
|
|
|
func TestDiffLines(t *testing.T) { |
|
cases := []struct { |
|
name, old, new, ptype string |
|
want []string |
|
}{ |
|
{"상태", "검토", "진행중", "status", []string{"[변경] 상태: 검토 → 진행중"}}, |
|
{"마감일", "", "2026-06-20", "date", []string{"[변경] 마감일: 2026-06-20 (신규)"}}, |
|
{"상태", "완료", "", "status", []string{"[삭제] 상태: 완료"}}, |
|
{"상태", "완료", "완료", "status", nil}, // 변화 없음 |
|
{"담당자", "김영운, 이철수", "김영운, 박지민", "people", []string{"[추가] 담당자: 박지민", "[삭제] 담당자: 이철수"}}, |
|
{"태그", "", "긴급", "multi_select", []string{"[추가] 태그: 긴급"}}, |
|
{"태그", "긴급, 배포", "긴급, 배포", "multi_select", nil}, // 동일 목록 |
|
} |
|
for _, tc := range cases { |
|
got := diffLines(tc.name, tc.old, tc.new, tc.ptype) |
|
if len(got) != len(tc.want) { |
|
t.Errorf("diffLines(%q,%q,%q,%q) = %v, want %v", tc.name, tc.old, tc.new, tc.ptype, got, tc.want) |
|
continue |
|
} |
|
for i := range tc.want { |
|
if got[i] != tc.want[i] { |
|
t.Errorf("diffLines[%d] = %q, want %q", i, got[i], tc.want[i]) |
|
} |
|
} |
|
} |
|
} |
|
|
|
func TestListDiff(t *testing.T) { |
|
add, rem := listDiff("a, b, c", "b, c, d") |
|
if len(add) != 1 || add[0] != "d" { |
|
t.Errorf("added = %v, want [d]", add) |
|
} |
|
if len(rem) != 1 || rem[0] != "a" { |
|
t.Errorf("removed = %v, want [a]", rem) |
|
} |
|
} |
|
|
|
func TestTruncRunes(t *testing.T) { |
|
if got := truncRunes("짧은글", 10); got != "짧은글" { |
|
t.Errorf("no-trunc = %q", got) |
|
} |
|
if got := truncRunes("가나다라마바사", 3); got != "가나다…" { |
|
t.Errorf("trunc = %q, want 가나다…", got) |
|
} |
|
} |
|
|
|
func TestSnapshotStoreRoundTrip(t *testing.T) { |
|
path := t.TempDir() + "/snap.json" |
|
s, err := NewSnapshotStore(path) |
|
if err != nil { |
|
t.Fatal(err) |
|
} |
|
s.Put("page1", snapshot{Title: "T", Props: map[string]string{"상태": "진행중"}}) |
|
|
|
// 다시 로드해도 유지되는지 (파일 영속화 확인) |
|
s2, err := NewSnapshotStore(path) |
|
if err != nil { |
|
t.Fatal(err) |
|
} |
|
got, ok := s2.Get("page1") |
|
if !ok || got.Props["상태"] != "진행중" { |
|
t.Fatalf("reload = %+v ok=%v", got, ok) |
|
} |
|
|
|
s2.Delete("page1") |
|
if _, ok := s2.Get("page1"); ok { |
|
t.Errorf("expected page1 deleted") |
|
} |
|
} |
|
|
|
func TestMentionUserIDs(t *testing.T) { |
|
mk := func(typ, uid, plain string) richText { |
|
rt := richText{Type: typ, PlainText: plain} |
|
if uid != "" { |
|
rt.Mention = &struct { |
|
Type string `json:"type"` |
|
User ref `json:"user"` |
|
}{Type: "user", User: ref{ID: uid}} |
|
} |
|
return rt |
|
} |
|
rt := []richText{ |
|
mk("text", "", "안녕 "), |
|
mk("mention", "u1", "@지대한"), |
|
mk("text", "", " 확인 부탁 "), |
|
mk("mention", "u2", "@김영운"), |
|
} |
|
got := mentionUserIDs(rt) |
|
if len(got) != 2 || got[0] != "u1" || got[1] != "u2" { |
|
t.Fatalf("mentionUserIDs = %v, want [u1 u2]", got) |
|
} |
|
// 멘션 없는 rich_text → 빈 결과 |
|
if ids := mentionUserIDs([]richText{mk("text", "", "그냥 텍스트")}); len(ids) != 0 { |
|
t.Errorf("expected no mentions, got %v", ids) |
|
} |
|
} |
|
|
|
func TestAssigneeIDs(t *testing.T) { |
|
c := &Client{assigneeProperties: []string{"담당자", "처리자", "참조인원"}} |
|
pg := &page{Properties: map[string]property{ |
|
"담당자": {Type: "people", People: []ref{{ID: "u1"}, {ID: "u2"}}}, |
|
"처리자": {Type: "people", People: []ref{{ID: "u2"}, {ID: "u3"}}}, // u2 중복 |
|
"참조인원": {Type: "people", People: []ref{{ID: "u4"}}}, |
|
"상태": {Type: "status", Status: &selectOption{Name: "진행중"}}, // people 아님 → 무시 |
|
}} |
|
got := c.assigneeIDs(pg) |
|
want := []string{"u1", "u2", "u3", "u4"} // 설정 순서대로, 중복 제거 |
|
if len(got) != len(want) { |
|
t.Fatalf("assigneeIDs = %v, want %v", got, want) |
|
} |
|
for i := range want { |
|
if got[i] != want[i] { |
|
t.Errorf("assigneeIDs[%d] = %q, want %q (%v)", i, got[i], want[i], got) |
|
} |
|
} |
|
} |
|
|
|
func TestEventLabel(t *testing.T) { |
|
cases := map[string]string{ |
|
"page.properties_updated": "속성 변경", |
|
"page.content_updated": "내용 변경", |
|
"comment.created": "새 댓글", |
|
"": "변경", |
|
"page.unknown": "page.unknown", |
|
} |
|
for in, want := range cases { |
|
if got := eventLabel(in); got != want { |
|
t.Errorf("eventLabel(%q) = %q, want %q", in, got, want) |
|
} |
|
} |
|
}
|
|
|