test(17-04): add failing tests for notification dispatcher
- TestSubscribeUnsubscribe: DB round-trip for add/remove subscriber - TestNotifyNewFindings_NoSubscribers: zero messages with empty table - TestNotifyNewFindings_ZeroFindings: no notification for 0 findings - TestFormatNotification: message contains job name, count, duration - TestFormatFindingNotification: masked key, never full key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
121
pkg/bot/subscribe_test.go
Normal file
121
pkg/bot/subscribe_test.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/salvacybersec/keyhunter/pkg/engine"
|
||||||
|
"github.com/salvacybersec/keyhunter/pkg/scheduler"
|
||||||
|
"github.com/salvacybersec/keyhunter/pkg/storage"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func openTestDB(t *testing.T) *storage.DB {
|
||||||
|
t.Helper()
|
||||||
|
db, err := storage.Open(":memory:")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { _ = db.Close() })
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscribeUnsubscribe(t *testing.T) {
|
||||||
|
db := openTestDB(t)
|
||||||
|
|
||||||
|
// Initially not subscribed.
|
||||||
|
ok, err := db.IsSubscribed(12345)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, ok, "should not be subscribed initially")
|
||||||
|
|
||||||
|
// Subscribe.
|
||||||
|
err = db.AddSubscriber(12345, "testuser")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ok, err = db.IsSubscribed(12345)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, ok, "should be subscribed after AddSubscriber")
|
||||||
|
|
||||||
|
// Unsubscribe.
|
||||||
|
rows, err := db.RemoveSubscriber(12345)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(1), rows, "should have removed 1 row")
|
||||||
|
|
||||||
|
ok, err = db.IsSubscribed(12345)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, ok, "should not be subscribed after RemoveSubscriber")
|
||||||
|
|
||||||
|
// Unsubscribe again returns 0 rows.
|
||||||
|
rows, err = db.RemoveSubscriber(12345)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(0), rows, "should have removed 0 rows when not subscribed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotifyNewFindings_NoSubscribers(t *testing.T) {
|
||||||
|
db := openTestDB(t)
|
||||||
|
|
||||||
|
b := &Bot{cfg: Config{DB: db}}
|
||||||
|
sent, errs := b.NotifyNewFindings(scheduler.JobResult{
|
||||||
|
JobName: "nightly-scan",
|
||||||
|
FindingCount: 5,
|
||||||
|
Duration: 10 * time.Second,
|
||||||
|
})
|
||||||
|
assert.Equal(t, 0, sent, "should send 0 messages with no subscribers")
|
||||||
|
assert.Empty(t, errs, "should have no errors with no subscribers")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotifyNewFindings_ZeroFindings(t *testing.T) {
|
||||||
|
db := openTestDB(t)
|
||||||
|
_ = db.AddSubscriber(12345, "user1")
|
||||||
|
|
||||||
|
b := &Bot{cfg: Config{DB: db}}
|
||||||
|
sent, errs := b.NotifyNewFindings(scheduler.JobResult{
|
||||||
|
JobName: "nightly-scan",
|
||||||
|
FindingCount: 0,
|
||||||
|
Duration: 3 * time.Second,
|
||||||
|
})
|
||||||
|
assert.Equal(t, 0, sent, "should not notify for zero findings")
|
||||||
|
assert.Empty(t, errs, "should have no errors for zero findings")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatNotification(t *testing.T) {
|
||||||
|
result := scheduler.JobResult{
|
||||||
|
JobName: "nightly-scan",
|
||||||
|
FindingCount: 7,
|
||||||
|
Duration: 2*time.Minute + 30*time.Second,
|
||||||
|
}
|
||||||
|
msg := formatNotification(result)
|
||||||
|
assert.Contains(t, msg, "nightly-scan", "message should contain job name")
|
||||||
|
assert.Contains(t, msg, "7", "message should contain finding count")
|
||||||
|
assert.Contains(t, msg, "2m30s", "message should contain duration")
|
||||||
|
assert.Contains(t, msg, "/stats", "message should reference /stats command")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatNotification_Error(t *testing.T) {
|
||||||
|
result := scheduler.JobResult{
|
||||||
|
JobName: "daily-scan",
|
||||||
|
FindingCount: 0,
|
||||||
|
Duration: 5 * time.Second,
|
||||||
|
Error: assert.AnError,
|
||||||
|
}
|
||||||
|
msg := formatErrorNotification(result)
|
||||||
|
assert.Contains(t, msg, "daily-scan", "error message should contain job name")
|
||||||
|
assert.Contains(t, msg, "error", "error message should indicate error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatFindingNotification(t *testing.T) {
|
||||||
|
finding := engine.Finding{
|
||||||
|
ProviderName: "OpenAI",
|
||||||
|
KeyValue: "sk-proj-1234567890abcdef",
|
||||||
|
KeyMasked: "sk-proj-...cdef",
|
||||||
|
Confidence: "high",
|
||||||
|
Source: "/tmp/test.py",
|
||||||
|
LineNumber: 42,
|
||||||
|
}
|
||||||
|
msg := formatFindingNotification(finding)
|
||||||
|
assert.Contains(t, msg, "OpenAI", "should contain provider name")
|
||||||
|
assert.Contains(t, msg, "sk-proj-...cdef", "should contain masked key")
|
||||||
|
assert.NotContains(t, msg, "sk-proj-1234567890abcdef", "should NOT contain full key")
|
||||||
|
assert.Contains(t, msg, "/tmp/test.py", "should contain source path")
|
||||||
|
assert.Contains(t, msg, "42", "should contain line number")
|
||||||
|
assert.Contains(t, msg, "high", "should contain confidence")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user