package csvimport import ( "strings" "testing" ) func TestParseOK(t *testing.T) { rows, err := Parse(strings.NewReader("a@x,p1,a@y,p2\nb@x,p3,b@y,p4\n")) if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 2 || rows[0].SrcLogin != "a@x" || rows[1].DstPass != "p4" { t.Fatalf("bad rows: %+v", rows) } } func TestParseRejectsBadColumns(t *testing.T) { if _, err := Parse(strings.NewReader("a,b,c\n")); err == nil { t.Fatal("3 columns must error") } } func TestParseRejectsDuplicateSrc(t *testing.T) { if _, err := Parse(strings.NewReader("a@x,p,a@y,p\na@x,q,c@y,q\n")); err == nil { t.Fatal("duplicate src_login must error") } } func TestParseRejectsEmptyField(t *testing.T) { if _, err := Parse(strings.NewReader("a@x,,a@y,p\n")); err == nil { t.Fatal("empty password must error") } } func TestParseBlankLineKeepsCorrectLineNumber(t *testing.T) { // blank physical line 2, malformed row on physical line 3 _, err := Parse(strings.NewReader("a@x,p1,a@y,p2\n\nbad,row,here\n")) if err == nil { t.Fatal("expected error for 3-column row") } if !strings.Contains(err.Error(), "line 3") { t.Fatalf("error must reference physical line 3, got: %v", err) } } func TestParseZeroRowsErrors(t *testing.T) { if _, err := Parse(strings.NewReader("\n\n \n")); err == nil { t.Fatal("expected error when no rows parsed") } } const kerioHeader = "Name;FullName;Description;Enable;DataSource;Authentication;Role;Groups;MailAddress\n" func TestParseKerioOK(t *testing.T) { in := kerioHeader + "j.doe;Jane Doe;SrcPass11;Yes;Internal;Internal;No rights;all;j.doe\n" + "k.smith;Kim Smith;SrcPass22;Yes;Internal;Internal;No rights;all;k.smith\n" rows, err := ParseKerio(strings.NewReader(in), "example.test") if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 2 { t.Fatalf("want 2 rows, got %d: %+v", len(rows), rows) } want := Row{ SrcLogin: "j.doe@example.test", SrcPass: "SrcPass11", DstLogin: "j.doe@example.test", DstPass: "SrcPass11", } if rows[0] != want { t.Fatalf("row 0: got %+v, want %+v", rows[0], want) } } func TestParseKerioSkipsDisabledAndAdmin(t *testing.T) { in := kerioHeader + "admin;;AdminPass1;Yes;Internal;Internal;Account admin;;admin\n" + "o.disabled;;OffPass111;No;Internal;Internal;No rights;all;o.disabled\n" + "info;;InfoPass11;Yes;Internal;Internal;No rights;all;info\n" rows, err := ParseKerio(strings.NewReader(in), "example.test") if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 1 || rows[0].SrcLogin != "info@example.test" { t.Fatalf("only the enabled non-admin row must survive, got %+v", rows) } } func TestParseKerioStripsBOM(t *testing.T) { in := "\ufeff" + kerioHeader + "info;;InfoPass11;Yes;Internal;Internal;No rights;all;info\n" rows, err := ParseKerio(strings.NewReader(in), "example.test") if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 1 { t.Fatalf("want 1 row, got %+v", rows) } } func TestParseKerioRejectsMissingHeader(t *testing.T) { in := "j.doe;Jane Doe;SrcPass11;Yes;Internal;Internal;No rights;all;j.doe\n" if _, err := ParseKerio(strings.NewReader(in), "example.test"); err == nil { t.Fatal("a file without the Kerio header must error") } } func TestParseKerioRejectsBadDomain(t *testing.T) { in := kerioHeader + "info;;InfoPass11;Yes;Internal;Internal;No rights;all;info\n" for _, domain := range []string{"", " ", "@example.test", "exam ple.test", "example", "info@example.test"} { if _, err := ParseKerio(strings.NewReader(in), domain); err == nil { t.Fatalf("domain %q must be rejected", domain) } } } func TestParseKerioTrimsAndLowercasesDomain(t *testing.T) { in := kerioHeader + "info;;InfoPass11;Yes;Internal;Internal;No rights;all;info\n" rows, err := ParseKerio(strings.NewReader(in), " Example.TEST ") if err != nil { t.Fatalf("parse: %v", err) } if rows[0].SrcLogin != "info@example.test" { t.Fatalf("domain must be trimmed and lowercased, got %q", rows[0].SrcLogin) } } func TestParseKerioRejectsEmptyPassword(t *testing.T) { in := kerioHeader + "info;;;Yes;Internal;Internal;No rights;all;info\n" if _, err := ParseKerio(strings.NewReader(in), "example.test"); err == nil { t.Fatal("an enabled account without a password must error") } } func TestParseKerioRejectsDuplicateName(t *testing.T) { in := kerioHeader + "info;;InfoPass11;Yes;Internal;Internal;No rights;all;info\n" + "info;;aaaaaaAa1;Yes;Internal;Internal;No rights;all;info\n" if _, err := ParseKerio(strings.NewReader(in), "example.test"); err == nil { t.Fatal("duplicate Name must error") } } func TestParseKerioZeroRowsErrors(t *testing.T) { in := kerioHeader + "admin;;AdminPass1;Yes;Internal;Internal;Account admin;;admin\n" if _, err := ParseKerio(strings.NewReader(in), "example.test"); err == nil { t.Fatal("expected error when every row is filtered out") } }