Files
miti99bot/cmd/server/main_test.go
T
tiennm99 53e569ca12 feat(blacklist): add /blacklist shorthand and /whitelist_rnd
/blacklist is the short form of the two read commands: bare it lists both
lists like /blacklist_rules, and given text it judges that text like
/blacklist_check. Both long names stay for when the intent should be
explicit. An argument of only whitespace is not an argument, so it lists.

/whitelist_rnd returns one whitelist entry chosen at random, and says the
list is empty rather than answering with nothing. It reads only the
whitelist, and only for the calling topic.

The six existing command descriptions are shortened alongside. /help
renders as one un-chunked message pinned at 4096 runes, and two more
commands pushed it to 4123; the shorter wording brings it to 4049. That
ceiling is a shared limit this module did not create, and it will bind again
on the next command added anywhere in the repo.
2026-09-15 13:58:45 +07:00

162 lines
5.5 KiB
Go

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) {
prev := gitSHA
defer func() { gitSHA = prev }()
// Coolify runtime env wins over the baked fallback.
gitSHA = "baked"
if got := resolveCommitSHA(" runtime-sha "); got != "runtime-sha" {
t.Errorf("env present: got %q, want trimmed runtime-sha", got)
}
// Empty env falls back to the VCS revision embedded by go build.
if got := resolveCommitSHA(""); got != "baked" {
t.Errorf("env empty: got %q, want baked fallback", got)
}
// Neither source set → "unknown" so the owner still gets the startup DM.
gitSHA = ""
if got := resolveCommitSHA(" "); got != "unknown" {
t.Errorf("both empty: got %q, want unknown", got)
}
}
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 TestInitStatsStoreRunsStartupMigration(t *testing.T) {
ctx := context.Background()
provider := storage.NewMemoryProvider()
if err := initStatsStore(ctx, provider); err != nil {
t.Fatalf("initStatsStore: %v", err)
}
marker, exists, err := systemstate.New(provider.Collection(systemstate.CollectionName)).Get(ctx, "migration:stats-delete-stock-dividend-v1")
if err != nil || !exists || marker.Status != "completed" {
t.Fatalf("marker=%+v exists=%v err=%v", marker, exists, 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 TestInitStatsStorePropagatesMigrationError(t *testing.T) {
want := errors.New("migration failed")
err := initStatsStoreWith(context.Background(), storage.NewMemoryProvider(), func(context.Context, storage.Collection, storage.Collection) error {
return want
})
if !errors.Is(err, want) {
t.Fatalf("initStatsStoreWith 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")
}
if got := shortCommitSHA("abc123"); got != "abc123" {
t.Errorf("short revision: got %q, want %q", got, "abc123")
}
}
func TestComposeDoesNotOverrideSourceCommit(t *testing.T) {
b, err := os.ReadFile(filepath.Join("..", "..", "compose.yml"))
if err != nil {
t.Fatalf("read compose.yml: %v", err)
}
for _, line := range strings.Split(string(b), "\n") {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
if strings.HasPrefix(trimmed, "SOURCE_COMMIT:") || strings.HasPrefix(trimmed, "- SOURCE_COMMIT") {
t.Fatalf("compose.yml must not declare SOURCE_COMMIT; Coolify supplies it at runtime and an explicit Compose value can override it with empty")
}
}
}
func TestFactoriesIncludesExpectedModules(t *testing.T) {
catalog := factories()
for _, name := range []string{"gold", "coin"} {
if catalog[name] == nil {
t.Fatalf("factories missing %s", name)
}
}
reg, err := modules.Build([]string{"gold", "coin"}, catalog, storage.NewMemoryProvider(), modules.BuildOptions{})
if err != nil {
t.Fatalf("Build selected modules: %v", err)
}
for _, name := range []string{
"gold_price", "gold_topup", "gold_buy", "gold_sell", "gold_portfolio",
"coin_price", "coin_topup", "coin_buy", "coin_sell", "coin_portfolio",
} {
if _, ok := reg.AllCommands[name]; !ok {
t.Fatalf("missing command %s", name)
}
}
}
func TestFactoriesRegistersBlacklistCommands(t *testing.T) {
catalog := factories()
if catalog["blacklist"] == nil {
t.Fatal("factories missing blacklist")
}
reg, err := modules.Build([]string{"blacklist"}, catalog, storage.NewMemoryProvider(), modules.BuildOptions{})
if err != nil {
t.Fatalf("Build blacklist: %v", err)
}
for _, name := range []string{
"blacklist", "blacklist_add", "blacklist_del", "blacklist_rules",
"blacklist_check", "whitelist_add", "whitelist_del", "whitelist_rnd",
} {
if _, ok := reg.AllCommands[name]; !ok {
t.Fatalf("missing command %s", name)
}
}
if got := len(reg.AllCommands); got != 8 {
t.Fatalf("blacklist registered %d commands, want 8", got)
}
}
// An empty MODULES loads every module, so a command name that collides with an
// existing module surfaces here as a test failure rather than as a startup
// crash on deploy.
func TestFactoriesBuildWholeCatalog(t *testing.T) {
if _, err := modules.Build(nil, factories(), storage.NewMemoryProvider(), modules.BuildOptions{}); err != nil {
t.Fatalf("Build whole catalog: %v", err)
}
}