import { describe, expect, it } from "vitest"; import { parseUserCSV } from "./csvParser"; describe("parseUserCSV", () => { it("should parse valid CSV correctly", () => { const csv = `email,name,phone,role,tenant,department,emp_id user1@test.com,Hong Gil Dong,010-1111-2222,user,baron,HR,E001 user2@test.com,Kim Cheol Su,,admin,baron,IT,E002`; const result = parseUserCSV(csv); expect(result).toHaveLength(2); expect(result[0]).toEqual({ email: "user1@test.com", name: "Hong Gil Dong", phone: "010-1111-2222", role: "user", tenantSlug: "baron", department: "HR", metadata: { emp_id: "E001", }, }); expect(result[1].email).toBe("user2@test.com"); expect(result[1].metadata.emp_id).toBe("E002"); }); it("should return empty array for empty input", () => { expect(parseUserCSV("")).toEqual([]); }); it("should skip rows without email or name", () => { const csv = `email,name ,Only Name no-name@test.com,`; expect(parseUserCSV(csv)).toHaveLength(0); }); it("should handle mixed case headers", () => { const csv = `EMAIL,Name,Tenant test@test.com,Test,baron`; const result = parseUserCSV(csv); expect(result[0].email).toBe("test@test.com"); expect(result[0].tenantSlug).toBe("baron"); }); it("should parse NAVERWORKS member CSV sample into Baron bulk user fields", () => { const csv = `"LastName","FirstName","ID","Personal email","Sub email","Nickname","User type","Level","Organization","Position","CompanyMainPhone","Mobile/Country code","Mobile/Numbers","Language","Responsibilities","Workplace","SNS","SNS_ID","Birthday (solar, lunar)","Birthday","Entry Date","Employee number","Account activation time" "Doe","John","john.doe","john@naver.com","john1@company.com; john2@company.com","John","Permanent Employee","Manager","org.1|org.2|org.3|myteam","Manager","02-0000-0000","+1","9144812222","English","Sales management","New York","Facebook","john","solar","19830415","20230415","AB001","20230415 08:00"`; const result = parseUserCSV(csv); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ email: "john1@company.com", loginId: "john.doe", name: "John Doe", phone: "+19144812222", department: "myteam", grade: "Manager", position: "Manager", jobTitle: "Sales management", tenantImport: { name: "myteam", parentTenantName: "org.3", }, metadata: { personal_email: "john@naver.com", employee_id: "AB001", naverworks_user_type: "Permanent Employee", naverworks_level: "Manager", naverworks_organization_path: "org.1|org.2|org.3|myteam", naverworks_workplace: "New York", }, }); }); it("should parse tenant conflict metadata for import resolution", () => { const csv = `email,name,tenant_id,tenant_slug,tenant_name,tenant_type,parent_tenant_slug,tenant_memo,email_domain test@test.com,Test,local-tenant-id,missing-slug,Missing Tenant,COMPANY,parent-slug,Imported memo,missing.example.com`; const result = parseUserCSV(csv); expect(result[0]).toMatchObject({ tenantId: "local-tenant-id", tenantSlug: "missing-slug", emailDomain: "missing.example.com", tenantImport: { sourceTenantId: "local-tenant-id", slug: "missing-slug", name: "Missing Tenant", type: "COMPANY", parentTenantSlug: "parent-slug", memo: "Imported memo", emailDomain: "missing.example.com", }, }); }); it("should ignore exported user_id during user CSV import", () => { const csv = `user_id,email,name,tenant_id,tenant_slug 9f8cc1b1-af8d-45d4-946c-924a529c2556,restore@test.com,Restore User,tenant-id,restore-tenant`; const result = parseUserCSV(csv); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ email: "restore@test.com", name: "Restore User", tenantId: "tenant-id", tenantSlug: "restore-tenant", }); expect(result[0]).not.toHaveProperty("id"); expect(result[0]).not.toHaveProperty("uuid"); }); it("should parse one nullable additional appointment from numbered columns", () => { const csv = `email,name,phone,role,tenant_slug,department,grade,position,jobTitle,employee_id,tenant_slug1,department1,grade1,position1,jobTitle1,employee_id1 dual@test.com,Dual User,010-0000-0000,user,primary-tenant,개발팀,책임,팀장,Backend,EMP001,second-tenant,센터,수석,,Architecture,EMP002 nullable@test.com,Nullable User,010-1111-1111,user,primary-tenant,개발팀,책임,팀장,Backend,EMP003,,,,,,`; const result = parseUserCSV(csv); expect(result).toHaveLength(2); expect(result[0]).toMatchObject({ tenantSlug: "primary-tenant", department: "개발팀", grade: "책임", position: "팀장", jobTitle: "Backend", metadata: { employee_id: "EMP001", }, additionalAppointments: [ { tenantSlug: "second-tenant", department: "센터", grade: "수석", jobTitle: "Architecture", metadata: { employee_id: "EMP002", }, }, ], }); expect(result[1].additionalAppointments).toBeUndefined(); }); it("should preserve sub_email as secondary email metadata without replacing primary email", () => { const csv = `email,name,tenant_slug,employee_id,sub_email primary@samaneng.com,Primary User,rnd-saman,EMP001,secondary@hanmaceng.co.kr`; const result = parseUserCSV(csv); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ email: "primary@samaneng.com", tenantSlug: "rnd-saman", metadata: { employee_id: "EMP001", sub_email: ["secondary@hanmaceng.co.kr"], aliasEmails: ["secondary@hanmaceng.co.kr"], }, }); }); it("should mark duplicate bulk alias emails as blocking import errors", () => { const csv = `email,name,tenant_slug,sub_email user1@samaneng.com,User One,rnd-saman,shared@hanmaceng.co.kr user2@samaneng.com,User Two,rnd-saman,shared@hanmaceng.co.kr`; const result = parseUserCSV(csv); expect(result).toHaveLength(2); expect(result[0].importErrors).toContain("duplicateEmail"); expect(result[1].importErrors).toContain("duplicateEmail"); }); it("should mark a primary email reused as a sub email as a blocking import error", () => { const csv = `email,name,tenant_slug,sub_email user1@samaneng.com,User One,rnd-saman,user2@samaneng.com user2@samaneng.com,User Two,rnd-saman,alias@hanmaceng.co.kr`; const result = parseUserCSV(csv); expect(result).toHaveLength(2); expect(result[0].importErrors).toContain("duplicateEmail"); expect(result[1].importErrors).toContain("duplicateEmail"); }); });