package httpapi import ( "io" "mime/multipart" "net/http" "net/http/httptest" "strings" "testing" "github.com/vasyansk/imap-copier/internal/config" ) func TestImportCSVFailsOnBadEncKey(t *testing.T) { // EncKey wrong size => crypto.Encrypt errors => handler must NOT return success s := &Server{cfg: config.Config{EncKey: make([]byte, 16)}} body := &strings.Builder{} mw := multipart.NewWriter(body) fw, _ := mw.CreateFormFile("file", "a.csv") fw.Write([]byte("a@x,p1,a@y,p2\n")) mw.Close() req := httptest.NewRequest("POST", "/api/tasks/1/import", strings.NewReader(body.String())) req.Header.Set("Content-Type", mw.FormDataContentType()) req.SetPathValue("id", "1") rw := httptest.NewRecorder() s.handleImportCSV(rw, req) if rw.Code == 200 || rw.Code == 201 { t.Fatalf("import must fail on bad EncKey, got %d", rw.Code) } } // importReq builds a multipart import request with the given CSV payload and // extra form fields, then returns it with the uploaded file ready to read. func importReq(t *testing.T, csv string, fields map[string]string) (*http.Request, io.Reader) { t.Helper() body := &strings.Builder{} mw := multipart.NewWriter(body) for k, v := range fields { _ = mw.WriteField(k, v) } fw, _ := mw.CreateFormFile("file", "a.csv") fw.Write([]byte(csv)) mw.Close() req := httptest.NewRequest("POST", "/api/tasks/1/import", strings.NewReader(body.String())) req.Header.Set("Content-Type", mw.FormDataContentType()) req.SetPathValue("id", "1") file, _, err := req.FormFile("file") if err != nil { t.Fatalf("form file: %v", err) } return req, file } const kerioCSV = "Name;FullName;Description;Enable;DataSource\n" + "info;;InfoPass11;Yes;Internal\n" func TestParseImportRowsUsesKerioParserWithDomain(t *testing.T) { req, file := importReq(t, kerioCSV, map[string]string{"format": "kerio", "domain": "example.test"}) rows, err := parseImportRows(req, file) if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 1 || rows[0].SrcLogin != "info@example.test" || rows[0].DstLogin != "info@example.test" { t.Fatalf("kerio rows must carry the supplied domain on both sides, got %+v", rows) } } func TestParseImportRowsKerioRequiresDomain(t *testing.T) { req, file := importReq(t, kerioCSV, map[string]string{"format": "kerio"}) if _, err := parseImportRows(req, file); err == nil { t.Fatal("kerio import without a domain must error") } } func TestParseImportRowsDefaultsToPlainFormat(t *testing.T) { req, file := importReq(t, "a@x,p1,a@y,p2\n", nil) rows, err := parseImportRows(req, file) if err != nil { t.Fatalf("parse: %v", err) } if len(rows) != 1 || rows[0].SrcLogin != "a@x" || rows[0].DstPass != "p2" { t.Fatalf("no format field must keep the 4-column parser, got %+v", rows) } } func TestParseRunAccountIDs(t *testing.T) { // empty body => nil (run all) req := httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader("")) ids, err := parseRunAccountIDs(req) if err != nil || ids != nil { t.Fatalf("empty body must yield nil ids, got %v err=%v", ids, err) } // explicit selection req = httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader(`{"account_ids":[3,7]}`)) ids, err = parseRunAccountIDs(req) if err != nil || len(ids) != 2 || ids[0] != 3 || ids[1] != 7 { t.Fatalf("must parse account_ids, got %v err=%v", ids, err) } // malformed JSON => error req = httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader(`{bad`)) if _, err := parseRunAccountIDs(req); err == nil { t.Fatal("malformed body must error") } }