dana-medical-site/internal/api/report_sberhealth_test.go
2026-10-14 13:33:13 +00:00

149 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"os"
"reflect"
"strings"
"testing"
"time"
"dana/internal/sberhealth"
)
func sberTestRows(records ...[]string) [][]string {
rows := [][]string{{"СберЗдоровье"}, {}, {}, {}, {}, {},
{"#", "Дата создания записи", "Дата приема", "Пациент", "Телефон", "Врач", "Специальность", "Предоплата", "Сумма предоплаты", "Статус", "Филиал", "Источник", "Причина отказа"}}
rows = append(rows, records...)
return append(rows, nil, []string{fmt.Sprintf("Количество элементов: %d", len(records))}, []string{"Дата формирования отчета: 05.10.2026 12:37"})
}
func TestSberHealthCreationDateDedupAndFooter(t *testing.T) {
from := time.Date(2026, 9, 28, 0, 0, 0, 0, moscow)
to := time.Date(2026, 10, 6, 0, 0, 0, 0, moscow)
phones, records, total, err := parseSberHealthRows(sberTestRows(
[]string{"1", "27.09.2026 23:59", "28.09.2026 12:00", "Тест", "79281230001"},
[]string{"2", "28.09.2026 00:00", "25.10.2026 12:00", "Тест", "+7 (928) 123-00-02¶"},
[]string{"3", "05.10.2026 23:59", "", "Тест", "89281230002"},
[]string{"4", "06.10.2026 00:00", "", "Тест", "79281230003"},
), from, to)
if err != nil || records != 2 || total != 4 || !reflect.DeepEqual(phones, []string{"9281230002"}) {
t.Fatal("creation filter, inclusive last day, dedup or footer incorrect", err, records, total)
}
empty, count, total, err := parseSberHealthRows(sberTestRows(), from, to)
if err != nil || len(empty) != 0 || count != 0 || total != 0 {
t.Fatal("valid empty report rejected", err)
}
}
func TestSberHealthInvalidAndIncompleteExports(t *testing.T) {
from := time.Date(2026, 10, 1, 0, 0, 0, 0, moscow)
to := from.AddDate(0, 1, 0)
valid := []string{"1", "05.10.2026 12:30", "", "Тест", "79281230001"}
truncated := sberTestRows(valid)
truncated[len(truncated)-2][0] = "Количество элементов: 20"
missingFooter := sberTestRows(valid)[:8]
duplicate := sberTestRows(valid, valid)
badPhone := sberTestRows([]string{"1", "05.10.2026 12:30", "", "Тест", "private-invalid-phone"})
badDate := sberTestRows([]string{"1", "private-invalid-date", "", "Тест", "79281230001"})
for _, rows := range [][][]string{truncated, missingFooter, duplicate, badPhone, badDate, {{"unknown format"}}} {
phones, _, _, err := parseSberHealthRows(rows, from, to)
if err == nil || len(phones) != 0 || strings.Contains(err.Error(), "private-invalid") {
t.Fatal("bad/truncated export accepted or private data leaked")
}
}
}
func TestSberHealthMergeUsesOnly1CRevenue(t *testing.T) {
from := time.Date(2026, 10, 1, 0, 0, 0, 0, moscow)
phones, records, total, err := parseSberHealthRows(sberTestRows(
[]string{"1", "05.10.2026 12:30", "", "Тест", "79281230001", "", "", "Да", "999999"},
[]string{"2", "05.10.2026 12:30", "", "Тест", "79281230002"},
), from, from.AddDate(0, 1, 0))
if err != nil {
t.Fatal(err)
}
book := xlsxBook(t, map[string][][]string{
"Лист_1": {{"Пациент", "Мобильный телефон", "Количество", "СуммаИтого"},
{"Тестов Один", "79281230001", "2", "1500"},
{"Другой Два", "79281230002", "1", "2000"},
{"Итого", "", "3", "3500"}},
"СберЗдоровье": {{"79281230001"}},
}, []string{"Лист_1", "СберЗдоровье"})
lists := mergeChannelLists(map[string][]string{"сберздоровье": {"89281230001"}}, map[string][]string{"сберздоровье": phones})
info := &sberhealthSourceInfo{Records: records, Phones: len(phones), ExportRecords: total}
_, result, err := (&API{}).phoneMatchOpts(context.Background(), &progressTracker{}, "book.xlsx", book, matchOpts{pbxLists: lists, sberhealthInfo: info})
if err != nil {
t.Fatal(err)
}
summary := result.(channelsSummary)
channel := channelByName(summary, "Сберздоровье")
if channel.Leads != 2 || channel.Patients != 2 || channel.Revenue != 3500 || channel.Services != 3 || summary.SberHealth != info {
t.Fatal("merge or 1C-only accounting incorrect")
}
}
// Opt-in local regression against the user's original sample. Never copied to
// testdata/repository, never printed, and no external requests are performed.
func TestSberHealthSuppliedXLSSample(t *testing.T) {
path := os.Getenv("DANA_SBER_SAMPLE_FILE")
if path == "" {
t.Skip("local source sample not supplied")
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatal("cannot read local sample")
}
rows, err := sberhealth.ReadXLS(data)
if err != nil {
t.Fatal(err)
}
from := time.Date(2026, 9, 28, 0, 0, 0, 0, moscow)
to := time.Date(2026, 10, 6, 0, 0, 0, 0, moscow)
phones, records, total, err := parseSberHealthRows(rows, from, to)
if err != nil {
t.Fatal(err)
}
if len(phones) != 6 || records != 7 || total != 7 {
t.Fatal("sample does not reconcile with independent XLS inspection")
}
// Exercise the real integration on local bytes through a fake transport,
// verifying the exclusive 1C end becomes an inclusive provider date.
previous := http.DefaultTransport
defer func() { http.DefaultTransport = previous }()
http.DefaultTransport = prodoctorovTestTransport(func(r *http.Request) (*http.Response, error) {
q := r.URL.Query()
if r.URL.Host != "docdoc.ru" || q.Get("clinic_id") != "294888" || q.Get("crDateFrom") != "28.09.2026" || q.Get("crDateTill") != "05.10.2026" || q.Get("periodInput") != "week" {
t.Fatal("incorrect inclusive provider date mapping")
}
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(bytes.NewReader(data))}, nil
})
c, _ := sberhealth.New("session=synthetic", "294888")
opts, err := (&API{sberhealth: c}).sberhealthMatchOpts(context.Background(), from, to)
if err != nil || opts.sberhealthInfo == nil || opts.sberhealthInfo.Records != 7 || opts.sberhealthInfo.Phones != 6 || opts.sberhealthInfo.ExportRecords != 7 {
t.Fatal("sample integration failed", err)
}
}
func TestSberHealthMonthlyRequestAndNoPartialDataOnFailure(t *testing.T) {
previous := http.DefaultTransport
defer func() { http.DefaultTransport = previous }()
http.DefaultTransport = prodoctorovTestTransport(func(r *http.Request) (*http.Response, error) {
q := r.URL.Query()
if q.Get("periodInput") != "month" || q.Get("crDateFrom") != "01.10.2026" || q.Get("crDateTill") != "31.10.2026" {
t.Fatal("monthly date conversion failed")
}
return &http.Response{StatusCode: 403, Header: make(http.Header), Body: http.NoBody}, nil
})
c, _ := sberhealth.New("session=synthetic", "294888")
from := time.Date(2026, 10, 1, 0, 0, 0, 0, moscow)
opts, err := (&API{sberhealth: c}).sberhealthMatchOpts(context.Background(), from, from.AddDate(0, 1, 0))
if err == nil || opts.sberhealthInfo != nil || len(opts.pbxLists) != 0 {
t.Fatal("failed download returned successful empty/partial options")
}
}