182 lines
3.9 KiB
Go
182 lines
3.9 KiB
Go
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)
|
||
|
|
}
|
||
|
|
}
|