package lkgateway // Живая проверка ЛК, ручного ввода и сверки переводов на Fancy на Firebird: функции выполняются на копии базы // клиента по договору с бумагами. Запуск: // BJ_FB_TEST_DSN='firebird://SYSDBA:пароль@127.0.0.1:3050/путь?charset=WIN1251' BJ_FB_TEST_CONTRACT='БР-1147' \ // BJ_FB_TEST_ACC='Б' BJ_FB_TEST_PASSPORT='3453 289090' BJ_FB_TEST_FIO='Бубликов Павел Иванович' \ // go test -run FBLiveLK -v ./internal/lkgateway import ( "context" "net/url" "os" "strconv" "strings" "testing" "time" ) func fbLiveSettings(t *testing.T) FancyMSSQLSettings { dsn := os.Getenv("BJ_FB_TEST_DSN") if dsn == "" || os.Getenv("BJ_FB_TEST_CONTRACT") == "" { t.Skip("BJ_FB_TEST_DSN / BJ_FB_TEST_CONTRACT не заданы") } u, err := url.Parse(dsn) if err != nil { t.Fatal(err) } pass, _ := u.User.Password() port, _ := strconv.Atoi(u.Port()) return FancyMSSQLSettings{DBType: "firebird", Server: u.Hostname(), Port: port, User: u.User.Username(), Password: pass, DepoDatabase: strings.TrimPrefix(u.Path, "/"), AccountPrefix: os.Getenv("BJ_FB_TEST_ACC")} } func TestFBLiveLK(t *testing.T) { s := fbLiveSettings(t) rc, err := NewRuntimeConfig(t.TempDir() + "/setup.json") if err != nil { t.Fatal(err) } if err := rc.UpdateFancy(s); err != nil { t.Fatal(err) } prev := globalRC globalRC = rc defer func() { globalRC = prev }() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() contract := os.Getenv("BJ_FB_TEST_CONTRACT") last := time.Now() check := func(what string, err error, n int, sample any) { took := time.Since(last) last = time.Now() if took > 5*time.Second { // ЛК ждёт ответа: на MSSQL эти чтения — доли секунды t.Errorf("%s: %v — медленно", what, took.Round(time.Millisecond)) } if err != nil { t.Errorf("%s: %v", what, err) return } t.Logf("%s (%v): %d — %+v", what, took.Round(time.Millisecond), n, sample) } first := func(n int, v any) any { if n == 0 { return nil } return v } name, ok := depoContractExists(ctx, s, contract) t.Logf("depoContractExists: %v %q", ok, name) if !ok { t.Errorf("договор %s не найден", contract) } pos, err := depoClientPositions(ctx, s, contract) check("depoClientPositions", err, len(pos), pos) lst, err := depoListContracts(ctx, s, "", 5) check("depoListContracts", err, len(lst), first(len(lst), lst)) fnd, err := depoFindContracts(ctx, s, contract[:2], 5) check("depoFindContracts", err, len(fnd), fnd) t.Logf("depoSuggestContracts: %v", depoSuggestContracts(ctx, s)) ids, face, accs, err := depoClientIDs(ctx, s, contract) check("depoClientIDs", err, len(ids), map[string]any{"face": face, "accounts": accs}) from, to := time.Now().AddDate(-1, 0, 0).Format("2006-01-02"), time.Now().Format("2006-01-02") ops, err := lkOperations(ctx, s, contract, from, to, 50) check("lkOperations", err, len(ops), first(len(ops), ops)) inc, tax, err := lkIncome(ctx, s, contract, from, to) check("lkIncome", err, len(inc), map[string]any{"tax": tax}) if s.SpectreDatabase == "" { // архив ЛК — из базы брокерского учёта SPECTRE; без неё — понятная ошибка _, err := lkArchivePortfolio(ctx, s, contract, time.Now()) t.Logf("lkArchivePortfolio без SPECTRE: %v", err) last = time.Now() } else { ap, err := lkArchivePortfolio(ctx, s, contract, time.Now()) check("lkArchivePortfolio", err, 1, ap) aops, err := lkArchiveOperations(ctx, s, contract, time.Now().AddDate(-1, 0, 0), time.Now()) check("lkArchiveOperations", err, len(aops), first(len(aops), aops)) } var isins []string var lkpos []LKPosition for _, p := range pos { isins = append(isins, p.ISIN) lkpos = append(lkpos, LKPosition{ISIN: p.ISIN, Name: p.SecurityName, Quantity: p.Quantity}) } ca, err := lkCorpActions(ctx, s, isins) check("lkCorpActions", err, len(ca), first(len(ca), ca)) docs, err := lkReadyDocuments(ctx, s, contract) check("lkReadyDocuments", err, len(docs), first(len(docs), docs)) up := lkUpcoming(ctx, s, lkpos, 400) check("lkUpcoming", nil, len(up), first(len(up), up)) fx := lkFxAccounting(ctx, s) check("lkFxAccounting", nil, len(fx), fx) if len(fx) == 0 { t.Error("lkFxAccounting: нет курсов валют") } for _, isin := range isins { card, err := securityCard(ctx, rc, isin, nil) check("securityCard "+isin, err, 1, card) } cur, cands, err := depoAccPrefixInfo(ctx, s) check("depoAccPrefixInfo", err, cur, cands) if req, err := depoDetectRequisites(ctx, s); err != nil { t.Errorf("depoDetectRequisites: %v", err) } else { t.Logf("depoDetectRequisites: %+v", *req) } ver, upd, err := depoVersion(ctx, s) t.Logf("depoVersion: %q %q %v", ver, upd, err) fio := strings.Fields(os.Getenv("BJ_FB_TEST_FIO")) pp := strings.Fields(os.Getenv("BJ_FB_TEST_PASSPORT")) if len(fio) == 3 && len(pp) == 2 { faces, msg := manualDepoSearch(rc, fio[0]) t.Logf("manualDepoSearch(%s): %d %s — %+v", fio[0], len(faces), msg, first(len(faces), faces)) if len(faces) == 0 { t.Errorf("manualDepoSearch: клиент %s не найден (%s)", fio[0], msg) } if len(pos) > 0 { id, msg := manualDepoClientByAccount(rc, pos[0].AccountNum) t.Logf("manualDepoClientByAccount(%s): %d %s", pos[0].AccountNum, id, msg) mp, msg := manualDepoPositions(rc, id) t.Logf("manualDepoPositions: %d %s — %+v", len(mp), msg, first(len(mp), mp)) } var secs []IncomingSecurity var outs []depoOutSec for i, p := range pos { ref := "FBREF" + strconv.Itoa(i) secs = append(secs, IncomingSecurity{ReferenceID: ref, ISIN: p.ISIN, Quantity: "1"}) outs = append(outs, depoOutSec{ReferenceID: ref, ISIN: p.ISIN, Qty: "1"}) } in := runDepoCheck(&IncomingTransfer{InvestorLast: fio[0], InvestorFirst: fio[1], InvestorMiddle: fio[2], DocSeries: pp[0], DocNumber: pp[1], Securities: secs}) t.Logf("runDepoCheck (зачисление): ran=%v err=%q investor=%q account=%q %s — %s", in.Ran, in.Err, in.Investor, in.Account, in.Recommend, in.Summary) out := runDepoCheckOut(fio[0], fio[1], fio[2], pp[0], pp[1], outs, "", "") t.Logf("runDepoCheckOut (списание): ran=%v err=%q investor=%q account=%q %s — %s", out.Ran, out.Err, out.Investor, out.Account, out.Recommend, out.Summary) if in.Err != "" || out.Err != "" || in.Investor == "" || out.Investor == "" || in.Account == "" || out.Account == "" { t.Errorf("сверка переводов: зачисление %q / списание %q, инвестор %q / %q", in.Err, out.Err, in.Investor, out.Investor) } code, contr, err := quikResolveClient(ctx, rc, pp[0], pp[1]) t.Logf("quikResolveClient: %q %q %v", code, contr, err) } }