package http import ( "context" "encoding/json" "errors" "net/http" "net/http/httptest" "strings" "testing" "primaya-api/cpone-middleware/internal/repository" ) type fakeLabRegistrationRepository struct { outcome repository.LabRegistrationOutcome err error params repository.LabRegistrationParams addParams repository.AddLabServicesParams packageParams repository.AddLabPackageParams regID string } func (f *fakeLabRegistrationRepository) RegisterLab(_ context.Context, params repository.LabRegistrationParams) (repository.LabRegistrationOutcome, error) { f.params = params return f.outcome, f.err } func (f *fakeLabRegistrationRepository) AddLabServices(_ context.Context, regID string, params repository.AddLabServicesParams) (repository.LabRegistrationOutcome, error) { f.regID = regID f.addParams = params return f.outcome, f.err } func (f *fakeLabRegistrationRepository) AddLabPackage(_ context.Context, regID string, params repository.AddLabPackageParams) (repository.LabRegistrationOutcome, error) { f.regID = regID f.packageParams = params return f.outcome, f.err } func TestRegisterLabValidPayloadAndResponseParity(t *testing.T) { doctorID := "DR01" registrationType := int64(3) repo := &fakeLabRegistrationRepository{outcome: repository.LabRegistrationOutcome{ HTTPStatus: http.StatusCreated, Success: true, Status: "registered", Message: "Registrasi laboratorium berhasil disimpan", Data: &repository.LabRegistrationData{ Registration: repository.LabRegistration{ RegID: "CPLAB2608180001", MEDRECID: "00001234", Tanggal: "2026-08-18 10:11:12", DepartemenID: "LAB", DokterID: &doctorID, JenisPasienID: 0, JenisRegID: ®istrationType, PeriodeTarifID: 10, }, Transaction: &repository.LabTransaction{ TrxDepartemenID: 99, TrxID: "CPLAB2608180001", GrandTotal: 0, }, Layanan: []repository.LabService{}, }, }} handler := Handler{labRepo: repo} body := `{ "medrec_id":"00001234", "tanggal":"2026-08-18", "departemen_id":"LAB", "dokter_id":"DR01", "perujuk":" Klinik A ", "jenis_pasien_id":"0", "periode_tarif_id":"10", "kelas_id":"", "diagnosa_kerja":"Pemeriksaan laboratorium", "catatan":" catatan ", "channel":" cpone-api " }` request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body)) recorder := httptest.NewRecorder() handler.RegisterLab(recorder, request) if recorder.Code != http.StatusCreated { t.Fatalf("status = %d, want %d; body=%s", recorder.Code, http.StatusCreated, recorder.Body.String()) } const expected = `{"success":true,"status":"registered","message":"Registrasi laboratorium berhasil disimpan","data":{"registration":{"RegID":"CPLAB2608180001","MEDRECID":"00001234","Tanggal":"2026-08-18 10:11:12","DepartemenID":"LAB","DokterID":"DR01","JenisPasienID":0,"JenisRegID":3,"PeriodeTarifID":10,"PerusahaanID":null,"AsuransiID":null,"ProviderID":null,"GroupTarifID":null,"NomerPolis":null},"transaction":{"TrxDepartemenID":99,"TrxID":"CPLAB2608180001","GrandTotal":0},"layanan":[]}}` + "\n" if recorder.Body.String() != expected { t.Fatalf("body mismatch\n got: %s\nwant: %s", recorder.Body.String(), expected) } if repo.params.MedrecID != "00001234" || repo.params.DepartmentID != "LAB" || repo.params.DoctorID != "DR01" { t.Fatalf("unexpected repository params: %+v", repo.params) } if repo.params.TariffPeriodID != 10 || repo.params.ClassID != "Default" || repo.params.Channel != "CPONE-API" { t.Fatalf("normalization mismatch: %+v", repo.params) } if repo.params.Referrer != " Klinik A " || repo.params.Notes != " catatan " { t.Fatalf("handler must preserve values for repository trimming: %+v", repo.params) } } func TestRegisterLabCompanyPayload(t *testing.T) { repo := &fakeLabRegistrationRepository{outcome: repository.LabRegistrationOutcome{ HTTPStatus: http.StatusCreated, Success: true, Status: "registered", Message: "Registrasi laboratorium berhasil disimpan", }} handler := Handler{labRepo: repo} body := `{ "medrec_id":"00409575", "departemen_id":"MED-PNJ-LAB", "dokter_id":"SPP01_003", "dokter_pengirim_id":"Luar", "perujuk":"DOKTER PERUJUK LUAR", "jenis_pasien_id":"1", "periode_tarif_id":7, "perusahaan_id":"PE260700001", "asuransi_id":"PE260700001", "provider_id":"", "group_tarif_id":1980, "nomer_polis":"POLIS-001", "nik_karyawan":"TSB2026002", "nama_karyawan":"INTAN SEPTIANI", "kelas_id":"Default", "shift_harian_id":48049, "diagnosa_kerja":"Test diagnosa" }` request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body)) recorder := httptest.NewRecorder() handler.RegisterLab(recorder, request) if recorder.Code != http.StatusCreated { t.Fatalf("status = %d, want %d; body=%s", recorder.Code, http.StatusCreated, recorder.Body.String()) } params := repo.params if params.PatientType != "1" || params.CompanyID != "PE260700001" || params.InsuranceID != "PE260700001" || params.GroupTariffID != 1980 { t.Fatalf("company guarantor params mismatch: %+v", params) } if params.PolicyNumber != "POLIS-001" || params.EmployeeID != "TSB2026002" || params.EmployeeName != "INTAN SEPTIANI" { t.Fatalf("company employee params mismatch: %+v", params) } } func TestRegisterLabValidationParity(t *testing.T) { baseline := map[string]any{ "medrec_id": "00001234", "departemen_id": "LAB", "dokter_id": "DR01", "dokter_pengirim_id": "DR02", "jenis_pasien_id": "0", "periode_tarif_id": 10, "diagnosa_kerja": "Pemeriksaan laboratorium", } tests := []struct { name string mutate func(map[string]any) message string }{ { name: "missing medrec", mutate: func(payload map[string]any) { delete(payload, "medrec_id") }, message: "Medrec ID wajib diisi", }, { name: "invalid date", mutate: func(payload map[string]any) { payload["tanggal"] = "18-08-2026" }, message: "Tanggal registrasi harus format Y-m-d", }, { name: "missing sender and referrer", mutate: func(payload map[string]any) { delete(payload, "dokter_pengirim_id") delete(payload, "perujuk") }, message: "Dokter pengirim atau perujuk wajib diisi", }, { name: "missing company for guarantor", mutate: func(payload map[string]any) { payload["jenis_pasien_id"] = "1" }, message: "Perusahaan wajib diisi untuk pasien penjamin", }, { name: "missing patient type", mutate: func(payload map[string]any) { delete(payload, "jenis_pasien_id") }, message: "Jenis pasien wajib diisi", }, { name: "invalid tariff period type", mutate: func(payload map[string]any) { payload["periode_tarif_id"] = "1.5" }, message: "The periode tarif id must be an integer.", }, { name: "short diagnosis", mutate: func(payload map[string]any) { payload["diagnosa_kerja"] = "AB" }, message: "Diagnosa kerja minimal 3 karakter", }, { name: "repeated diagnosis", mutate: func(payload map[string]any) { payload["diagnosa_kerja"] = "---" }, message: "Diagnosa kerja tidak valid", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { payload := make(map[string]any, len(baseline)) for key, value := range baseline { payload[key] = value } test.mutate(payload) body, err := json.Marshal(payload) if err != nil { t.Fatal(err) } handler := Handler{labRepo: &fakeLabRegistrationRepository{}} request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(string(body))) recorder := httptest.NewRecorder() handler.RegisterLab(recorder, request) if recorder.Code != http.StatusUnprocessableEntity { t.Fatalf("status = %d; body=%s", recorder.Code, recorder.Body.String()) } expected := `{"success":false,"status":"validation_error","message":` + strconvQuote(test.message) + `,"data":null}` + "\n" if recorder.Body.String() != expected { t.Fatalf("body mismatch\n got: %s\nwant: %s", recorder.Body.String(), expected) } }) } } func TestRegisterLabRepositoryErrorsMatchLaravelEnvelope(t *testing.T) { tests := []struct { name string err error statusCode int status string message string }{ { name: "domain validation", err: repository.ValidationError{Message: "Departemen tujuan bukan departemen laboratorium aktif"}, statusCode: http.StatusUnprocessableEntity, status: "validation_error", message: "Departemen tujuan bukan departemen laboratorium aktif", }, { name: "database error", err: errors.New("database unavailable"), statusCode: http.StatusInternalServerError, status: "error", message: "database unavailable", }, } const validBody = `{"medrec_id":"00001234","departemen_id":"LAB","dokter_id":"DR01","dokter_pengirim_id":"DR02","jenis_pasien_id":"0","periode_tarif_id":10,"diagnosa_kerja":"Pemeriksaan laboratorium"}` for _, test := range tests { t.Run(test.name, func(t *testing.T) { handler := Handler{labRepo: &fakeLabRegistrationRepository{err: test.err}} request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(validBody)) recorder := httptest.NewRecorder() handler.RegisterLab(recorder, request) if recorder.Code != test.statusCode { t.Fatalf("status = %d; body=%s", recorder.Code, recorder.Body.String()) } expected := `{"success":false,"status":` + strconvQuote(test.status) + `,"message":` + strconvQuote(test.message) + `,"data":null}` + "\n" if recorder.Body.String() != expected { t.Fatalf("body mismatch\n got: %s\nwant: %s", recorder.Body.String(), expected) } }) } } func strconvQuote(value string) string { encoded, _ := json.Marshal(value) return string(encoded) }