- Implemented Citations component to display citation data. - Created WatchlistDrawer and ChatSidebar components for managing watchlists and AI chat functionality. - Integrated API calls for watchlist management and chat interactions. - Updated index.tsx to include new components in the main application layout. - Added API client in lib/api.ts for structured API interactions. - Developed Alerts, Dashboard, Portfolio, Routines, Screener, and Report pages with relevant data fetching and UI components. - Introduced styles in tokens.css for consistent theming across the application. - Configured TypeScript and Vite for project setup and development.
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package routines
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"flowsight/internal/alerts"
|
|
"flowsight/internal/store"
|
|
)
|
|
|
|
func seedEngine(t *testing.T) *Engine {
|
|
t.Helper()
|
|
db, err := store.Open(t.TempDir() + "/routines.db")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
if _, err := db.SeedFromDir("../../tests/fixtures", "demo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return &Engine{DB: db, Notifier: alerts.NewNotifier("", "", ""), UserKey: "demo"}
|
|
}
|
|
|
|
// Briefing generates from seed with zero empty sections.
|
|
func TestBriefing(t *testing.T) {
|
|
e := seedEngine(t)
|
|
text, cites, err := e.Briefing(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sec := range []string{"Top accumulation", "Foreign flow", "Agenda"} {
|
|
if !strings.Contains(text, sec) {
|
|
t.Fatalf("missing section %q", sec)
|
|
}
|
|
}
|
|
if len(cites) == 0 {
|
|
t.Fatal("briefing has no citations")
|
|
}
|
|
}
|
|
|
|
// Radar fires on the accumulation fixture.
|
|
func TestRadar(t *testing.T) {
|
|
e := seedEngine(t)
|
|
text, _, err := e.Radar(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(text, "BBCA") {
|
|
t.Fatalf("radar missed BBCA: %s", text)
|
|
}
|
|
}
|
|
|
|
// Accuracy resolution math: weights follow the ledger.
|
|
func TestAccuracyWeight(t *testing.T) {
|
|
e := seedEngine(t)
|
|
if w := e.DB.AccuracyWeight("smart-money"); w != 0.5 {
|
|
t.Fatalf("default weight = %v, want 0.5", w)
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
_ = e.DB.RecordPrediction("smart-money", "BBCA", "BUY")
|
|
}
|
|
rows, _ := e.DB.DueForResolution()
|
|
_ = rows
|
|
}
|
|
|
|
// Briefing carries agenda + mover sections and respects the 25-line cap.
|
|
func TestBriefingAgendaMoverCap(t *testing.T) {
|
|
e := seedEngine(t)
|
|
text, cites, err := e.Briefing(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sec := range []string{"Top accumulation", "Foreign flow", "Agenda", "Mover of the day"} {
|
|
if !strings.Contains(text, sec) {
|
|
t.Fatalf("missing section %q:\n%s", sec, text)
|
|
}
|
|
}
|
|
if n := len(strings.Split(strings.TrimRight(text, "\n"), "\n")); n > 25 {
|
|
t.Fatalf("briefing = %d lines, want ≤25", n)
|
|
}
|
|
if len(cites) == 0 {
|
|
t.Fatal("briefing has no citations")
|
|
}
|
|
}
|
|
|
|
// R5/R6 run without error on seed (windows simply miss today).
|
|
func TestCountdownsRun(t *testing.T) {
|
|
e := seedEngine(t)
|
|
rows, _ := e.DB.ListRoutines("demo")
|
|
_ = rows
|
|
if _, _, err := e.EarningsCountdown(context.Background()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := e.DividendCalendar(context.Background()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|