package http import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "primaya-api/cpone-middleware/internal/repository" ) type fakeTarifKerjasamaRepository struct { page repository.TarifKerjasamaPage detail *repository.TarifKerjasamaDetail err error listParams repository.TarifKerjasamaListParams detailParams repository.TarifKerjasamaDetailParams detailID int64 } func (f *fakeTarifKerjasamaRepository) ListTarifKerjasama(_ context.Context, params repository.TarifKerjasamaListParams) (repository.TarifKerjasamaPage, error) { f.listParams = params return f.page, f.err } func (f *fakeTarifKerjasamaRepository) GetTarifKerjasama(_ context.Context, id int64, params repository.TarifKerjasamaDetailParams) (*repository.TarifKerjasamaDetail, error) { f.detailID = id f.detailParams = params return f.detail, f.err } func TestListTarifKerjasamaResponseAndFilters(t *testing.T) { kode, pic := "INS-001", "Bapak A" repo := &fakeTarifKerjasamaRepository{page: repository.TarifKerjasamaPage{ Total: 16, Items: []repository.TarifKerjasamaSummary{{ PerusahaanTarifID: 1098, PerusahaanID: "COMP-001", KodePerusahaan: &kode, NamaPerusahaan: "24 Medicare", PIC: &pic, JumlahGroupTarif: 2, }}, }} handler := Handler{tarifRepo: repo} request := httptest.NewRequest(http.MethodGet, "http://localhost/api/cpone/tarif-kerjasama?search=medicare&perusahaan_id=COMP-001&tarif_id=1098&page=1&per_page=15", nil) recorder := httptest.NewRecorder() handler.ListTarifKerjasama(recorder, request) if recorder.Code != http.StatusOK { t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String()) } if repo.listParams.Search != "medicare" || repo.listParams.PerusahaanID != "COMP-001" || repo.listParams.TarifID == nil || *repo.listParams.TarifID != 1098 { t.Fatalf("params mismatch: %+v", repo.listParams) } for _, expected := range []string{ `"PerusahaanTarifID":1098`, `"NamaPerusahaan":"24 Medicare"`, `"JumlahGroupTarif":2`, `"last_page":2`, } { if !strings.Contains(recorder.Body.String(), expected) { t.Fatalf("response does not contain %s: %s", expected, recorder.Body.String()) } } } func TestGetTarifKerjasamaNestedResponse(t *testing.T) { layananID, namaLayanan := "LAB-001", "Darah Lengkap" kelasID, namaKelas := "KLS-1", "Kelas I" detail := &repository.TarifKerjasamaDetail{ TarifKerjasamaSummary: repository.TarifKerjasamaSummary{ PerusahaanTarifID: 1098, PerusahaanID: "COMP-001", NamaPerusahaan: "24 Medicare", JumlahGroupTarif: 1, }, GroupTarif: []repository.TarifKerjasamaGroup{{ GroupTarifID: 501, PerusahaanTarifID: 1098, NamaGroupTarif: "Tarif Corporate 2026", NA: "N", Aktif: true, JumlahLayanan: 1, Layanan: []repository.TarifKerjasamaLayanan{{ LayananID: &layananID, NamaLayanan: &namaLayanan, JumlahDetail: 1, Detail: []repository.TarifKerjasamaLayananDetail{{ TarifLayananID: 1, KelasID: &kelasID, NamaKelas: &namaKelas, Harga: 125000, }}, }}, }}, } repo := &fakeTarifKerjasamaRepository{detail: detail} handler := Handler{tarifRepo: repo} request := httptest.NewRequest(http.MethodGet, "/api/cpone/tarif-kerjasama/1098?group_tarif_id=501&effective_date=2026-08-22", nil) request.SetPathValue("id", "1098") recorder := httptest.NewRecorder() handler.GetTarifKerjasama(recorder, request) if recorder.Code != http.StatusOK || repo.detailID != 1098 { t.Fatalf("status=%d id=%d body=%s", recorder.Code, repo.detailID, recorder.Body.String()) } if repo.detailParams.GroupTarifID == nil || *repo.detailParams.GroupTarifID != 501 || repo.detailParams.Active != "N" || repo.detailParams.EffectiveDate != "2026-08-22" { t.Fatalf("params mismatch: %+v", repo.detailParams) } for _, expected := range []string{ `"status":"tarif_kerjasama_found"`, `"GroupTarifID":501`, `"LayananID":"LAB-001"`, `"Harga":125000`, } { if !strings.Contains(recorder.Body.String(), expected) { t.Fatalf("response does not contain %s: %s", expected, recorder.Body.String()) } } } func TestTarifKerjasamaValidationAndNotFound(t *testing.T) { tests := []struct { path string id string }{ {"/api/cpone/tarif-kerjasama/abc", "abc"}, {"/api/cpone/tarif-kerjasama/1?group_tarif_id=0", "1"}, {"/api/cpone/tarif-kerjasama/1?active=X", "1"}, {"/api/cpone/tarif-kerjasama/1?effective_date=22-08-2026", "1"}, } for _, test := range tests { request := httptest.NewRequest(http.MethodGet, test.path, nil) request.SetPathValue("id", test.id) recorder := httptest.NewRecorder() Handler{tarifRepo: &fakeTarifKerjasamaRepository{}}.GetTarifKerjasama(recorder, request) if recorder.Code != http.StatusUnprocessableEntity || !strings.Contains(recorder.Body.String(), `"status":"validation_error"`) { t.Fatalf("path=%s status=%d body=%s", test.path, recorder.Code, recorder.Body.String()) } } request := httptest.NewRequest(http.MethodGet, "/api/cpone/tarif-kerjasama/9999", nil) request.SetPathValue("id", "9999") recorder := httptest.NewRecorder() Handler{tarifRepo: &fakeTarifKerjasamaRepository{}}.GetTarifKerjasama(recorder, request) if recorder.Code != http.StatusNotFound || !strings.Contains(recorder.Body.String(), `"status":"tarif_kerjasama_not_found"`) { t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String()) } } func TestTarifKerjasamaRepositoryErrorsAreHidden(t *testing.T) { handler := Handler{tarifRepo: &fakeTarifKerjasamaRepository{err: errors.New("database unavailable")}} request := httptest.NewRequest(http.MethodGet, "/api/cpone/tarif-kerjasama", nil) recorder := httptest.NewRecorder() handler.ListTarifKerjasama(recorder, request) if recorder.Code != http.StatusInternalServerError || recorder.Body.String() != `{"success":false,"message":"internal server error","data":null}`+"\n" { t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String()) } }