package prodoctorov import ( "bytes" "context" "io" "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestConfig(t *testing.T) { c, err := New("", "") if err != nil || c.Configured() { t.Fatal("empty configuration must disable the connector") } for _, args := range [][2]string{{"", "82347"}, {"session=test", ""}, {"session=x\r\nInjected: bad", "82347"}, {"session=test", "-1"}, {"session=test", "82347&lpu=other"}, {"Cookie", "82347"}} { if _, err := New(args[0], args[1]); err == nil { t.Fatal("malformed configuration accepted") } } } func TestDownloadQueryAndCookie(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/cabinet/lpu/appointments/direct/download/" || r.Method != "GET" || r.URL.Query().Get("month") != "10" || r.URL.Query().Get("year") != "2026" || r.URL.Query().Get("lpu") != "82347" || r.Header.Get("Cookie") != "session=synthetic" { t.Error("wrong export request") } _, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'}) })) defer s.Close() c, _ := New("session=synthetic", "82347") c.endpoint = s.URL + "/api/cabinet/lpu/appointments/direct/download/" data, err := c.Download(context.Background(), 2026, time.October) if err != nil || !bytes.HasPrefix(data, []byte("PK")) { t.Fatal("valid export failed", err) } } func TestDownloadErrorsDoNotLeakResponse(t *testing.T) { for _, status := range []int{200, 401, 403, 429, 500} { t.Run(http.StatusText(status), func(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) _, _ = io.WriteString(w, "patient-secret session=synthetic") })) defer s.Close() c, _ := New("session=synthetic", "82347") c.endpoint = s.URL _, err := c.Download(context.Background(), 2026, time.October) if err == nil || strings.Contains(err.Error(), "patient-secret") || strings.Contains(err.Error(), "synthetic") { t.Fatal("missing or leaking error") } }) } } func TestDownloadProtectionIsNotExpiredSession(t *testing.T) { for _, tc := range []struct { name string status int body string want string }{ {"forbidden", http.StatusForbidden, "private challenge", "HTTP 403, ответ через Servicepipe"}, {"challenge page", http.StatusOK, "private challenge", "другая страница через Servicepipe"}, } { t.Run(tc.name, func(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-SP-CRID", "private-request-id") w.WriteHeader(tc.status) _, _ = io.WriteString(w, tc.body) })) defer s.Close() c, _ := New("session=synthetic", "82347") c.endpoint = s.URL _, err := c.Download(context.Background(), 2026, time.October) if err == nil || !strings.Contains(err.Error(), tc.want) || strings.Contains(err.Error(), "private") || strings.Contains(err.Error(), "synthetic") { t.Fatal("protection was misreported or secret data leaked") } }) } } func TestDownloadProtectionHeaderDoesNotRejectXLSX(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-SP-CRID", "synthetic-request-id") _, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'}) })) defer s.Close() c, _ := New("session=synthetic", "82347") c.endpoint = s.URL if _, err := c.Download(context.Background(), 2026, time.October); err != nil { t.Fatal("valid export rejected solely because of proxy header", err) } } func TestRedirectDoesNotForwardSession(t *testing.T) { for _, sameHost := range []bool{false, true} { calls := 0 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ })) var source *httptest.Server source = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/login" { calls++ return } location := target.URL if sameHost { location = source.URL + "/login" } http.Redirect(w, r, location, http.StatusFound) })) c, _ := New("session=synthetic", "82347") c.endpoint = source.URL _, err := c.Download(context.Background(), 2026, time.October) source.Close() target.Close() if err == nil || calls != 0 { t.Fatal("redirect followed or accepted") } } } func TestDownloadBoundAndCancellation(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(append([]byte{'P', 'K', 3, 4}, bytes.Repeat([]byte{'x'}, maxDownload)...)) })) defer s.Close() c, _ := New("session=synthetic", "82347") c.endpoint = s.URL if _, err := c.Download(context.Background(), 2026, time.October); err == nil { t.Fatal("oversized export accepted") } ctx, cancel := context.WithCancel(context.Background()) cancel() if _, err := c.Download(ctx, 2026, time.October); err != context.Canceled { t.Fatal("cancellation lost", err) } }