Files

182 lines
3.9 KiB
Go
Raw Permalink Normal View History

package app
import (
"errors"
"sync"
"testing"
"time"
"it2000.com.cn/tele-recv/config"
"it2000.com.cn/tele-recv/storage"
)
// mockSender implements transport.Sender for testing.
type mockSender struct {
mu sync.Mutex
sent []string
sendErr error
closeErr error
}
func (m *mockSender) Send(telegram string) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.sendErr != nil {
return m.sendErr
}
m.sent = append(m.sent, telegram)
return nil
}
func (m *mockSender) Close() error { return m.closeErr }
// mockStore implements storage insertion for testing.
type mockStore struct {
mu sync.Mutex
telegrams []string
insertErr error
}
func (m *mockStore) Insert(telegram string) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.insertErr != nil {
return m.insertErr
}
m.telegrams = append(m.telegrams, telegram)
return nil
}
func (m *mockStore) LoadUnprocessed() ([]storage.Telegram, error) {
return nil, nil
}
func (m *mockStore) MarkProcessed(id int64) error {
return nil
}
func (m *mockStore) Close() error { return nil }
func TestNewApp_NilConfig(t *testing.T) {
// Should not panic, but serial port open will fail later
cfg := &config.Config{
Serial: config.SerialConfig{
Device: "/nonexistent",
Baudrate: 9600,
},
SQLite: config.SQLiteConfig{
File: ":memory:",
Init: true,
},
Log: config.LogConfig{
Dir: t.TempDir(),
MaxAge: 1,
RotateHour: 24,
},
}
a, err := New(cfg)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
if a == nil {
t.Fatal("expected non-nil app")
}
}
func TestDispatch_StoreAndSend(t *testing.T) {
store := &mockStore{}
sender := &mockSender{}
cfg := &config.Config{
SQLite: config.SQLiteConfig{File: ":memory:"},
Log: config.LogConfig{Dir: t.TempDir(), MaxAge: 1, RotateHour: 24},
}
app, err := New(cfg)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
app.store = store
app.sender = sender
app.dispatch("ZCZC TEST NNNN")
if len(store.telegrams) != 1 {
t.Errorf("expected 1 stored telegram, got %d", len(store.telegrams))
}
if len(sender.sent) != 1 {
t.Errorf("expected 1 sent telegram, got %d", len(sender.sent))
}
}
func TestDispatch_StoreError(t *testing.T) {
store := &mockStore{insertErr: errors.New("db error")}
sender := &mockSender{}
cfg := &config.Config{
SQLite: config.SQLiteConfig{File: ":memory:"},
Log: config.LogConfig{Dir: t.TempDir(), MaxAge: 1, RotateHour: 24},
}
app, err := New(cfg)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
app.store = store
app.sender = sender
// Should not panic on store error, should still send
app.dispatch("ZCZC TEST NNNN")
if len(sender.sent) != 1 {
t.Errorf("expected 1 sent telegram despite store error, got %d", len(sender.sent))
}
}
func TestDispatch_SendError(t *testing.T) {
store := &mockStore{}
sender := &mockSender{sendErr: errors.New("send error")}
cfg := &config.Config{
SQLite: config.SQLiteConfig{File: ":memory:"},
Log: config.LogConfig{Dir: t.TempDir(), MaxAge: 1, RotateHour: 24},
}
app, err := New(cfg)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
app.store = store
app.sender = sender
// Should not panic on send error, should still store
app.dispatch("ZCZC TEST NNNN")
if len(store.telegrams) != 1 {
t.Errorf("expected 1 stored telegram despite send error, got %d", len(store.telegrams))
}
}
func TestRun_CancelledContext(t *testing.T) {
cfg := &config.Config{
Serial: config.SerialConfig{
Device: "/nonexistent",
Baudrate: 9600,
},
SQLite: config.SQLiteConfig{File: ":memory:"},
Log: config.LogConfig{Dir: t.TempDir(), MaxAge: 1, RotateHour: 24},
}
app, err := New(cfg)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
// Cancel after a short delay so Run() tries opening port and exits cleanly
go func() {
time.Sleep(50 * time.Millisecond)
app.cancel()
}()
err = app.Run()
if err != nil {
t.Fatalf("Run() should return nil on cancellation, got: %v", err)
}
}