feat(stock): persist per-user dividend history

This commit is contained in:
tiennm99 committed 2026-07-22 16:17:29 +07:00
1 parent 8de459b131
commit df240a5db0
25 files changed
+2076 -582

No files matched your search

+25
View File
@@ -29,6 +29,7 @@ import (
"github.com/tiennm99/miti99bot/internal/modules/wordle"
"github.com/tiennm99/miti99bot/internal/server"
"github.com/tiennm99/miti99bot/internal/storage"
"github.com/tiennm99/miti99bot/internal/systemstate"
"github.com/tiennm99/miti99bot/internal/telegram"
)
@@ -94,6 +95,10 @@ func factories() map[string]modules.Factory {
// container; 10s leaves headroom without hiding a wedged cluster.
const mongodbInitTimeout = 10 * time.Second
// stockMigrationTimeout bounds the one-time scan of persisted stock
// portfolios without tying it to the shorter MongoDB connection timeout.
const stockMigrationTimeout = 2 * time.Minute
func main() {
rootCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
@@ -119,6 +124,12 @@ func main() {
if err := lol.InitStore(rootCtx, provider.Collection(lol.CollectionName)); err != nil {
log.Fatal("lol storage init failed", "err", err)
}
migrationCtx, cancelMigration := context.WithTimeout(rootCtx, stockMigrationTimeout)
if err := initStockStore(migrationCtx, provider); err != nil {
cancelMigration()
log.Fatal("stock storage init failed", "err", err)
}
cancelMigration()
b, err := telegram.NewBot(cfg.TelegramBotToken)
if err != nil {
@@ -209,6 +220,20 @@ func main() {
}
}
func initStockStore(ctx context.Context, provider storage.Provider) error {
return initStockStoreWith(ctx, provider, stock.InitStore)
}
type stockStoreInitializer func(context.Context, storage.Collection, storage.Collection) error
func initStockStoreWith(ctx context.Context, provider storage.Provider, init stockStoreInitializer) error {
return init(
ctx,
provider.Collection(stock.CollectionName),
provider.Collection(systemstate.CollectionName),
)
}
// buildProvider picks the storage backend. Selection order:
// 1. Explicit KV_PROVIDER env (memory|mongodb) wins.
// 2. Auto-detect: MONGO_URL set → mongodb; otherwise memory.
+29
View File
@@ -1,13 +1,17 @@
package main
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"github.com/tiennm99/miti99bot/internal/modules"
"github.com/tiennm99/miti99bot/internal/modules/stock"
"github.com/tiennm99/miti99bot/internal/storage"
"github.com/tiennm99/miti99bot/internal/systemstate"
)
func TestResolveCommitSHA(t *testing.T) {
@@ -32,6 +36,31 @@ func TestResolveCommitSHA(t *testing.T) {
}
}
func TestInitStockStoreRunsStartupMigration(t *testing.T) {
ctx := context.Background()
provider := storage.NewMemoryProvider()
if err := initStockStore(ctx, provider); err != nil {
t.Fatalf("initStockStore: %v", err)
}
marker, exists, err := systemstate.New(provider.Collection(systemstate.CollectionName)).Get(ctx, "migration:stock-dividend-history-v1")
if err != nil || !exists || marker.Status != "completed" {
t.Fatalf("marker=%+v exists=%v err=%v", marker, exists, err)
}
if _, _, err := storage.Typed[stock.Portfolio](provider.Collection(stock.CollectionName)).Get(ctx, "user:1"); !errors.Is(err, storage.ErrNotFound) {
t.Fatalf("unexpected portfolio lookup error: %v", err)
}
}
func TestInitStockStorePropagatesMigrationError(t *testing.T) {
want := errors.New("migration failed")
err := initStockStoreWith(context.Background(), storage.NewMemoryProvider(), func(context.Context, storage.Collection, storage.Collection) error {
return want
})
if !errors.Is(err, want) {
t.Fatalf("initStockStoreWith error=%v, want %v", err, want)
}
}
func TestShortCommitSHA(t *testing.T) {
if got := shortCommitSHA(" 0123456789abcdef "); got != "0123456" {
t.Errorf("full revision: got %q, want %q", got, "0123456")