diff --git a/cmd/parasocial/main_test.go b/cmd/parasocial/main_test.go new file mode 100644 index 0000000..e230923 --- /dev/null +++ b/cmd/parasocial/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func BenchmarkMain(b *testing.B) { + // Not really useful for entire application start +} diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index 1f1acc1..eda5865 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -323,3 +323,17 @@ func containsLine(lines []string, want string) bool { } return false } + +func BenchmarkHasScope(b *testing.B) { + scopes := []string{"chat:read", "chat:edit", "whispers:read", "user:read:email"} + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = hasScope(scopes, "whispers:read") + } +} + +func BenchmarkRandomAlphaNumeric(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = randomAlphaNumeric(32) + } +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2a953b7..4df034b 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -73,3 +73,19 @@ func writeConfig(t *testing.T, content string) string { } return path } + +func BenchmarkNormalizeStreamers(b *testing.B) { + streamers := []string{" StreamerOne ", "streamerTwo", "", "streamerOne", "https://twitch.tv/streamerThree"} + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = normalizeStreamers(streamers) + } +} + +func BenchmarkNormalizeStreamer(b *testing.B) { + streamer := " https://www.twitch.tv/StreamerName " + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = normalizeStreamer(streamer) + } +} diff --git a/internal/gql/client_test.go b/internal/gql/client_test.go index 455667a..f756aac 100644 --- a/internal/gql/client_test.go +++ b/internal/gql/client_test.go @@ -157,3 +157,15 @@ func TestPersistedOperationHashes(t *testing.T) { t.Fatalf("stream info hash = %q", got) } } + +func BenchmarkFormatErrors(b *testing.B) { + errs := []Error{ + {Message: "first error"}, + {Message: "second error"}, + {Message: "third error"}, + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = formatErrors(errs) + } +} diff --git a/internal/irc/client_test.go b/internal/irc/client_test.go index de542a8..303e244 100644 --- a/internal/irc/client_test.go +++ b/internal/irc/client_test.go @@ -221,3 +221,19 @@ func TestAuthFailureReturnsError(t *testing.T) { t.Fatalf("err = %v", err) } } + +func BenchmarkPingPayload(b *testing.B) { + line := "PING :tmi.twitch.tv" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = pingPayload(line) + } +} + +func BenchmarkIsJoinConfirmation(b *testing.B) { + line := ":jules!jules@jules.tmi.twitch.tv JOIN #parasocial" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = isJoinConfirmation(line, "jules", "parasocial") + } +} diff --git a/internal/miner/pubsub_test.go b/internal/miner/pubsub_test.go index ad78f8d..aad9dcc 100644 --- a/internal/miner/pubsub_test.go +++ b/internal/miner/pubsub_test.go @@ -49,3 +49,19 @@ func TestParseFrameVideoPlaybackFallsBackToTopicSuffix(t *testing.T) { t.Fatalf("event = %#v", frame.Event) } } + +func BenchmarkParseFramePointsEarned(b *testing.B) { + msg := []byte(`{"type":"MESSAGE","data":{"topic":"community-points-user-v1.viewer","message":"{\"type\":\"points-earned\",\"data\":{\"timestamp\":\"2026-04-29T12:00:00Z\",\"balance\":{\"balance\":42,\"channel_id\":\"123\"}}}"}}`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = parseFrame(msg) + } +} + +func BenchmarkParseFrameClaimAvailable(b *testing.B) { + msg := []byte(`{"type":"MESSAGE","data":{"topic":"community-points-user-v1.viewer","message":"{\"type\":\"claim-available\",\"data\":{\"timestamp\":\"2026-04-29T12:00:00Z\",\"claim\":{\"id\":\"claim-1\",\"channel_id\":\"123\"}}}"}}`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = parseFrame(msg) + } +} diff --git a/internal/tui/model_bench_test.go b/internal/tui/model_bench_test.go new file mode 100644 index 0000000..8e085a2 --- /dev/null +++ b/internal/tui/model_bench_test.go @@ -0,0 +1,28 @@ +package tui + +import "testing" +import "parasocial/internal/twitch" + +func BenchmarkFormatIRCChatLine(b *testing.B) { + line := ":jules!jules@jules.tmi.twitch.tv PRIVMSG #parasocial :Hello world this is a test message" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = formatIRCChatLine(line) + } +} + +func BenchmarkNormalizeKey(b *testing.B) { + key := "SomeStreamerName" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = normalizeKey(key) + } +} + +func BenchmarkIsActive(b *testing.B) { + entry := twitch.StreamerEntry{Status: twitch.StreamerReady} + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = isActive(entry) + } +} diff --git a/internal/twitch/service_bench_test.go b/internal/twitch/service_bench_test.go new file mode 100644 index 0000000..663d3ae --- /dev/null +++ b/internal/twitch/service_bench_test.go @@ -0,0 +1,35 @@ +package twitch + +import "testing" + +func BenchmarkBuildMinuteWatchedPayload(b *testing.B) { + userID := "user123" + channelID := "channel456" + login := "streamer" + metadata := &StreamMetadata{ + BroadcastID: "bc123", + Game: &Game{Name: "Science", ID: "123"}, + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = BuildMinuteWatchedPayload(userID, channelID, login, metadata) + } +} + +func BenchmarkEncodeMinuteWatchedPayload(b *testing.B) { + payload := MinuteWatchedPayload{ + { + Event: "minute_watched", + Properties: minuteWatchedProperties{ + ChannelID: "channel456", + BroadcastID: "bc123", + Player: "site", + UserID: "user123", + }, + }, + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = payload.Encode() + } +}