package onec import ( "context" "errors" "net/http" "net/http/httptest" "testing" "time" ) func newFake(t *testing.T, h http.HandlerFunc) *Client { t.Helper() srv := httptest.NewServer(h) t.Cleanup(srv.Close) return New(srv.URL, "secret", "") } func TestClientSendsKeyAndParsesBothShapes(t *testing.T) { c := newFake(t, func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Api-Key") != "secret" { http.Error(w, `{"code":"unauthorized","message":"нет ключа"}`, http.StatusUnauthorized) return } switch r.URL.Path { case "/branches": _, _ = w.Write([]byte(`[{"id":"b1","name":"Ляхова, 22","address":"ул. Ляхова, 22"}]`)) case "/doctors": _, _ = w.Write([]byte(`{"doctors":[{"id":"d1","name":"Иванов Иван Иванович","branch_ids":["b1"],"active":false}]}`)) case "/schedule": if r.URL.Query().Get("doctor_id") != "d1" { t.Errorf("doctor_id не передан: %s", r.URL.RawQuery) } _, _ = w.Write([]byte(`{"slots":[{"doctor_id":"d1","branch_id":"b1","start":"2026-10-01T10:00:00+03:00","end":"2026-10-01T10:30:00+03:00","status":"free"}]}`)) } }) ctx := context.Background() br, err := c.Branches(ctx) if err != nil || len(br) != 1 || br[0].ID != "b1" { t.Fatalf("branches: %v %+v", err, br) } docs, err := c.Doctors(ctx) if err != nil || len(docs) != 1 || docs[0].IsActive() { t.Fatalf("doctors: %v %+v", err, docs) } from := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC) slots, err := c.Schedule(ctx, from, from.Add(24*time.Hour), "d1") if err != nil || len(slots) != 1 || !slots[0].Free() { t.Fatalf("schedule: %v %+v", err, slots) } bad := New(c.Base, "wrong", "") if _, err := bad.Branches(ctx); !errors.Is(err, ErrUnauthorized) { t.Fatalf("ждали ErrUnauthorized, получили %v", err) } } func TestBookErrors(t *testing.T) { cases := []struct { status int body string want error }{ {409, `{"code":"slot_busy","message":"Окно занято"}`, ErrSlotBusy}, {404, `{"error":{"code":"doctor_not_found","message":"нет врача"}}`, ErrDoctorNotFound}, {400, `{"code":"validation","message":"нет телефона"}`, ErrValidation}, {500, `oops`, ErrUnavailable}, } for _, tc := range cases { c := newFake(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(tc.status) _, _ = w.Write([]byte(tc.body)) }) _, err := c.Book(context.Background(), AppointmentRequest{DoctorID: "d1"}) if !errors.Is(err, tc.want) { t.Errorf("HTTP %d: ждали %v, получили %v", tc.status, tc.want, err) } } } func TestBookSuccess(t *testing.T) { c := newFake(t, func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost || r.URL.Path != "/appointments" { t.Errorf("не тот запрос: %s %s", r.Method, r.URL.Path) } w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"appointment_id":"A-1","status":"booked","start":"2026-10-01T10:00:00+03:00","end":"2026-10-01T10:30:00+03:00"}`)) }) a, err := c.Book(context.Background(), AppointmentRequest{DoctorID: "d1", Source: "Сайт"}) if err != nil || a.ID != "A-1" || a.Status != "booked" { t.Fatalf("book: %v %+v", err, a) } }