0

Add tests, add makefile targets - test and help

This commit is contained in:
eleksir
2026-03-30 17:05:16 +03:00
parent 77bddef812
commit 2ef9199c9a
4 changed files with 1044 additions and 0 deletions

View File

@@ -8,6 +8,19 @@ UNIX_BINARY=${MYNAME}
WINDOWS_BINARY=${MYNAME}.exe WINDOWS_BINARY=${MYNAME}.exe
RMCMD=rm -rf RMCMD=rm -rf
# Default target
.PHONY: help
help:
@echo "aleesa-misc-go build system"
@echo ""
@echo "Available targets:"
@echo " all Clean and build (default)"
@echo " build Build binary only"
@echo " clean Remove built artifacts"
@echo " test Run unit tests"
@echo " upgrade Update dependencies (get, tidy, vendor)"
@echo ""
# На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe # На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe
# не нужен # не нужен
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
@@ -30,6 +43,7 @@ define IFS
endef endef
.PHONY: all build clean test upgrade help
all: clean build all: clean build
@@ -69,6 +83,10 @@ else # not ifeq ($(OS),Windows_NT)
endif endif
test:
go test ./...
upgrade: upgrade:
go get -u ./... go get -u ./...
go mod tidy go mod tidy

View File

@@ -0,0 +1,351 @@
package misc
import (
"context"
"encoding/json"
"strings"
"testing"
)
func TestMsgParser_Validation(t *testing.T) {
tests := []struct {
name string
inputJSON string
expectPanic bool
}{
{
name: "empty message should not panic",
inputJSON: `{"from":"test","chatid":"1","userid":"1","message":"","plugin":"test","mode":"test"}`,
expectPanic: false,
},
{
name: "short message less than csign",
inputJSON: `{"from":"test","chatid":"1","userid":"1","message":"!","plugin":"test","mode":"test","misc":{"csign":"!!!"}}`,
expectPanic: false,
},
{
name: "valid message with single char csign",
inputJSON: `{"from":"test","chatid":"1","userid":"1","message":"!karma","plugin":"test","mode":"test","misc":{"csign":"!"}}`,
expectPanic: false,
},
{
name: "message shorter than multi char csign",
inputJSON: `{"from":"test","chatid":"1","userid":"1","message":"!","plugin":"test","mode":"test","misc":{"csign":"!!"}}`,
expectPanic: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if !tt.expectPanic {
t.Errorf("unexpected panic: %v", r)
}
}
}()
// We only test that parsing doesn't panic
// Actual publishing requires Redis
var msg rMsg
if err := json.Unmarshal([]byte(tt.inputJSON), &msg); err != nil {
t.Logf("JSON parse error (expected for some tests): %v", err)
}
})
}
}
func TestMsgParser_CommandRouting(t *testing.T) {
// Save original config
origConfig := Config
Config = myConfig{
Csign: "!",
ForwardChannels: struct {
Games string `json:"games,omitempty"`
Phrases string `json:"phrases,omitempty"`
Webapp string `json:"webapp,omitempty"`
WebappGo string `json:"webapp-go,omitempty"`
Craniac string `json:"craniac,omitempty"`
}{
Games: "games",
Phrases: "phrases",
Webapp: "webapp",
WebappGo: "webapp-go",
Craniac: "craniac",
},
}
defer func() { Config = origConfig }()
tests := []struct {
name string
message string
expected string
}{
// Phrases commands
{"friday command", "!friday", "phrases"},
{"fortune command", "!fortune", "phrases"},
{"karma command", "!karma", "phrases"},
{"russian friday", "!пятница", "phrases"},
{"russian karma", "!карма", "phrases"},
// Webapp-go commands
{"frog command", "!frog", "webapp-go"},
{"cat command", "!cat", "webapp-go"},
{"weather command", "!w Moskow", "webapp-go"},
{"russian weather", "!погода Москва", "webapp-go"},
// Games commands
{"dig command", "!dig", "games"},
{"fish command", "!fish", "games"},
{"russian dig", "!копать", "games"},
// Default fallback
{"unknown command", "!unknown", "craniac"},
{"no command prefix", "just some text", "craniac"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg := rMsg{
From: "test",
Chatid: "1",
Userid: "1",
Message: tt.message,
Plugin: "test",
Mode: "test",
Misc: struct {
Answer int64 `json:"answer,omitempty"`
Botnick string `json:"bot_nick,omitempty"`
Csign string `json:"csign,omitempty"`
Fwdcnt *int64 `json:"fwd_cnt,omitempty"`
GoodMorning int64 `json:"good_morning,omitempty"`
Msgformat int64 `json:"msg_format,omitempty"`
Username string `json:"username,omitempty"`
}{
Csign: Config.Csign,
Fwdcnt: new(int64),
},
}
*msg.Misc.Fwdcnt = 1
result := determineChannel(msg.Message, msg.Misc.Csign)
if result != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, result)
}
})
}
}
func TestMsgParser_KarmaPatterns(t *testing.T) {
origConfig := Config
Config = myConfig{
Csign: "!",
ForwardChannels: struct {
Games string `json:"games,omitempty"`
Phrases string `json:"phrases,omitempty"`
Webapp string `json:"webapp,omitempty"`
WebappGo string `json:"webapp-go,omitempty"`
Craniac string `json:"craniac,omitempty"`
}{
Phrases: "phrases",
Craniac: "craniac",
},
}
defer func() { Config = origConfig }()
tests := []struct {
name string
message string
expected string
}{
{"karma increment", "something++", "phrases"},
{"karma decrement", "something--", "phrases"},
{"russian plus plus", "что-то++", "phrases"},
{"no karma pattern", "just text", "craniac"},
{"multiple lines no karma", "line1\nline2++", "craniac"},
{"single line with karma", "line1++", "phrases"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := determineKarmaChannel(tt.message)
if result != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, result)
}
})
}
}
func TestMsgParser_FwdcntPointer(t *testing.T) {
// Test that Fwdcnt pointer handling works correctly
fwdcnt := new(int64)
*fwdcnt = 0
if fwdcnt == nil {
t.Error("fwdcnt should not be nil after new(int64)")
}
if *fwdcnt != 0 {
t.Errorf("expected 0, got %d", *fwdcnt)
}
*fwdcnt++
if *fwdcnt != 1 {
t.Errorf("expected 1, got %d", *fwdcnt)
}
}
// determineChannel is extracted logic for testing command routing
func determineChannel(message, csign string) string {
sendTo := "craniac"
if len(message) >= len(csign) && message[0:len(csign)] == csign {
cmd := message[len(csign):]
cmds := []string{"friday", "пятница", "proverb", "пословица", "пословиться", "fortune", "фортунка", "f", "ф",
"karma", "карма", "rum", "ром", "vodka", "водка", "beer", "пиво", "tequila", "текила", "whisky", "виски",
"absinthe", "абсент", "fuck"}
for _, command := range cmds {
if cmd == command {
return "phrases"
}
}
cmds = []string{"frog", "лягушка", "horse", "лошадь", "лошадка", "rabbit", "bunny", "кролик",
"snail", "улитка", "cat", "кис", "fox", "лис", "buni", "anek", "анек", "анекдот",
"xkcd", "monkeyuser", "tits", "boobs", "tities", "boobies", "сиси", "сисечки", "butt",
"booty", "ass", "попа", "попка", "drink", "праздник", "owl", "сова", "сыч", "w", "п", "погода",
"погодка", "погадка"}
for _, command := range cmds {
if cmd == command {
return "webapp-go"
}
}
cmds = []string{"dig", "копать", "fish", "fishing", "рыба", "рыбка", "рыбалка"}
for _, command := range cmds {
if cmd == command {
return "games"
}
}
cmdLen := len(cmd)
cmds = []string{"w ", "п ", "погода ", "погодка ", "погадка ", "weather "}
for _, command := range cmds {
if cmdLen > len(command) && cmd[0:len(command)] == command {
return "webapp-go"
}
}
cmds = []string{"karma ", "карма ", "rum ", "ром ", "vodka ", "водка ", "beer ", "пиво ", "tequila ",
"текила ", "whisky ", "виски ", "absinthe ", "абсент "}
for _, command := range cmds {
if cmdLen > len(command) && cmd[0:len(command)] == command {
return "phrases"
}
}
}
return sendTo
}
// determineKarmaChannel is extracted logic for testing karma patterns
func determineKarmaChannel(message string) string {
msgLen := len(message)
if msgLen > 2 {
if message[msgLen-2:] == "--" || message[msgLen-2:] == "++" {
// Предполагается, что менять карму мы будем для одной фразы, то есть для 1 строки
if len(strings.Split(message, "\n")) == 1 {
return "phrases"
}
}
}
return "craniac"
}
func TestMsgParser_BuildsMsgCorrectly(t *testing.T) {
// Test that message building produces correct output
fwdcnt := new(int64)
*fwdcnt = 2
input := rMsg{
From: "telegram",
Chatid: "123",
Userid: "456",
Threadid: "789",
Message: "!karma user",
Plugin: "telegram",
Mode: "private",
Misc: struct {
Answer int64 `json:"answer,omitempty"`
Botnick string `json:"bot_nick,omitempty"`
Csign string `json:"csign,omitempty"`
Fwdcnt *int64 `json:"fwd_cnt,omitempty"`
GoodMorning int64 `json:"good_morning,omitempty"`
Msgformat int64 `json:"msg_format,omitempty"`
Username string `json:"username,omitempty"`
}{
Answer: 1,
Botnick: "aleesa",
Csign: "!",
Fwdcnt: fwdcnt,
Username: "testuser",
},
}
// Simulate the message building logic
var message sMsg
message.From = input.From
message.Userid = input.Userid
message.Chatid = input.Chatid
message.Threadid = input.Threadid
message.Message = input.Message
message.Plugin = input.Plugin
message.Mode = input.Mode
message.Misc.Fwdcnt = *input.Misc.Fwdcnt
message.Misc.Csign = input.Misc.Csign
message.Misc.Username = input.Misc.Username
message.Misc.Answer = input.Misc.Answer
message.Misc.Botnick = input.Misc.Botnick
message.Misc.Msgformat = input.Misc.Msgformat
message.Misc.GoodMorning = input.Misc.GoodMorning
// Verify
if message.Misc.Fwdcnt != 2 {
t.Errorf("expected Fwdcnt 2, got %d", message.Misc.Fwdcnt)
}
if message.Misc.Answer != 1 {
t.Errorf("expected Answer 1, got %d", message.Misc.Answer)
}
if message.Misc.Username != "testuser" {
t.Errorf("expected Username 'testuser', got %s", message.Misc.Username)
}
// Verify JSON serialization works
data, err := json.Marshal(message)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
// Verify we can unmarshal it back
var result sMsg
if err := json.Unmarshal(data, &result); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
if result.From != message.From {
t.Errorf("From mismatch: got %s", result.From)
}
if result.Misc.Fwdcnt != message.Misc.Fwdcnt {
t.Errorf("Fwdcnt mismatch: got %d", result.Misc.Fwdcnt)
}
}
var _ = context.Background // for import

View File

@@ -0,0 +1,327 @@
package misc
import (
"encoding/json"
"testing"
)
func TestConfigValidation(t *testing.T) {
tests := []struct {
name string
config myConfig
expectedServer string
expectedPort int
expectValid bool
}{
{
name: "full valid config",
config: myConfig{
Server: "redis.example.com",
Port: 6380,
Channel: "test-channel",
Csign: "/",
},
expectedServer: "redis.example.com",
expectedPort: 6380,
expectValid: true,
},
{
name: "config with defaults",
config: myConfig{
Channel: "test-channel",
Csign: "!",
},
expectedServer: "localhost",
expectedPort: 6379,
expectValid: true,
},
{
name: "missing channel",
config: myConfig{
Server: "localhost",
Port: 6379,
Csign: "!",
},
expectValid: false,
},
{
name: "missing csign",
config: myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
},
expectValid: false,
},
{
name: "zero port uses default",
config: myConfig{
Server: "localhost",
Port: 0,
Channel: "test",
Csign: "!",
},
expectedServer: "localhost",
expectedPort: 6379,
expectValid: true,
},
{
name: "zero timeout uses default",
config: myConfig{
Server: "localhost",
Port: 6379,
Timeout: 0,
Channel: "test",
Csign: "!",
},
expectedServer: "localhost",
expectedPort: 6379,
expectValid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.config
valid := validateConfig(&cfg)
if valid != tt.expectValid {
t.Errorf("expected valid=%v, got %v", tt.expectValid, valid)
}
if tt.expectValid {
applyDefaults(&cfg)
if cfg.Server != tt.expectedServer {
t.Errorf("expected Server=%s, got %s", tt.expectedServer, cfg.Server)
}
if cfg.Port != tt.expectedPort {
t.Errorf("expected Port=%d, got %d", tt.expectedPort, cfg.Port)
}
}
})
}
}
func TestForwardChannelsDefaults(t *testing.T) {
cfg := myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
Csign: "!",
}
applyDefaults(&cfg)
if cfg.ForwardChannels.Games != "games" {
t.Errorf("expected games='games', got '%s'", cfg.ForwardChannels.Games)
}
if cfg.ForwardChannels.Phrases != "phrases" {
t.Errorf("expected phrases='phrases', got '%s'", cfg.ForwardChannels.Phrases)
}
if cfg.ForwardChannels.Webapp != "webapp" {
t.Errorf("expected webapp='webapp', got '%s'", cfg.ForwardChannels.Webapp)
}
if cfg.ForwardChannels.WebappGo != "webapp-go" {
t.Errorf("expected webapp-go='webapp-go', got '%s'", cfg.ForwardChannels.WebappGo)
}
if cfg.ForwardChannels.Craniac != "craniac" {
t.Errorf("expected craniac='craniac', got '%s'", cfg.ForwardChannels.Craniac)
}
}
func TestForwardChannelsCustomValues(t *testing.T) {
cfg := myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
Csign: "!",
ForwardChannels: struct {
Games string `json:"games,omitempty"`
Phrases string `json:"phrases,omitempty"`
Webapp string `json:"webapp,omitempty"`
WebappGo string `json:"webapp-go,omitempty"`
Craniac string `json:"craniac,omitempty"`
}{
Games: "my-games",
Phrases: "my-phrases",
Webapp: "my-webapp",
WebappGo: "my-webapp-go",
Craniac: "my-craniac",
},
}
applyDefaults(&cfg)
if cfg.ForwardChannels.Games != "my-games" {
t.Errorf("expected games='my-games', got '%s'", cfg.ForwardChannels.Games)
}
if cfg.ForwardChannels.Phrases != "my-phrases" {
t.Errorf("expected phrases='my-phrases', got '%s'", cfg.ForwardChannels.Phrases)
}
}
// validateConfig simulates the validation logic from ReadConfig
func validateConfig(cfg *myConfig) bool {
if cfg.Channel == "" {
return false
}
if cfg.Csign == "" {
return false
}
return true
}
// applyDefaults simulates the default application logic from ReadConfig
func applyDefaults(cfg *myConfig) {
if cfg.Server == "" {
cfg.Server = "localhost"
}
if cfg.Port == 0 {
cfg.Port = 6379
}
if cfg.Timeout == 0 {
cfg.Timeout = 10
}
if cfg.Loglevel == "" {
cfg.Loglevel = "info"
}
if cfg.ForwardChannels.Games == "" {
cfg.ForwardChannels.Games = "games"
}
if cfg.ForwardChannels.Phrases == "" {
cfg.ForwardChannels.Phrases = "phrases"
}
if cfg.ForwardChannels.Webapp == "" {
cfg.ForwardChannels.Webapp = "webapp"
}
if cfg.ForwardChannels.WebappGo == "" {
cfg.ForwardChannels.WebappGo = "webapp-go"
}
if cfg.ForwardChannels.Craniac == "" {
cfg.ForwardChannels.Craniac = "craniac"
}
if cfg.Csign == "" {
cfg.Csign = "!"
}
if cfg.ForwardsMax == 0 {
cfg.ForwardsMax = 5
}
}
func TestLoglevelValidation(t *testing.T) {
tests := []struct {
name string
loglevel string
expectedOK bool
}{
{"valid error", "error", true},
{"valid warn", "warn", true},
{"valid info", "info", true},
{"valid debug", "debug", true},
{"invalid level", "trace", false},
{"empty uses default", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
Csign: "!",
}
if tt.loglevel != "" {
cfg.Loglevel = tt.loglevel
}
applyDefaults(cfg)
// Valid loglevels should result in a non-empty loglevel
// Invalid ones would still get set to "info" as default
if cfg.Loglevel == "" && tt.expectedOK {
t.Error("expected non-empty loglevel")
}
})
}
}
func TestForwardsMaxDefault(t *testing.T) {
cfg := myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
Csign: "!",
}
applyDefaults(&cfg)
if cfg.ForwardsMax != 5 {
t.Errorf("expected ForwardsMax=5, got %d", cfg.ForwardsMax)
}
cfg.ForwardsMax = 10
applyDefaults(&cfg)
if cfg.ForwardsMax != 10 {
t.Errorf("expected ForwardsMax=10, got %d", cfg.ForwardsMax)
}
}
func TestConfigJSONRoundtrip(t *testing.T) {
original := myConfig{
Server: "redis.example.com",
Port: 6380,
Timeout: 15,
Loglevel: "debug",
Log: "/var/log/test.log",
Channel: "test-channel",
Csign: "/",
ForwardsMax: 10,
ForwardChannels: struct {
Games string `json:"games,omitempty"`
Phrases string `json:"phrases,omitempty"`
Webapp string `json:"webapp,omitempty"`
WebappGo string `json:"webapp-go,omitempty"`
Craniac string `json:"craniac,omitempty"`
}{
Games: "custom-games",
Phrases: "custom-phrases",
Webapp: "custom-webapp",
WebappGo: "custom-webapp-go",
Craniac: "custom-craniac",
},
}
// Marshal
data, err := json.Marshal(original)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
// Unmarshal
var restored myConfig
if err := json.Unmarshal(data, &restored); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
// Compare
if restored.Server != original.Server {
t.Errorf("Server: expected %s, got %s", original.Server, restored.Server)
}
if restored.Port != original.Port {
t.Errorf("Port: expected %d, got %d", original.Port, restored.Port)
}
if restored.Channel != original.Channel {
t.Errorf("Channel: expected %s, got %s", original.Channel, restored.Channel)
}
if restored.Csign != original.Csign {
t.Errorf("Csign: expected %s, got %s", original.Csign, restored.Csign)
}
if restored.ForwardsMax != original.ForwardsMax {
t.Errorf("ForwardsMax: expected %d, got %d", original.ForwardsMax, restored.ForwardsMax)
}
if restored.ForwardChannels.Games != original.ForwardChannels.Games {
t.Errorf("ForwardChannels.Games: expected %s, got %s", original.ForwardChannels.Games, restored.ForwardChannels.Games)
}
}

348
internal/misc/types_test.go Normal file
View File

@@ -0,0 +1,348 @@
package misc
import (
"encoding/json"
"testing"
)
func TestRMsgJSONDeserialization(t *testing.T) {
tests := []struct {
name string
jsonInput string
expectedFrom string
expectedChatid string
expectedPlugin string
expectCsignNil bool
}{
{
name: "full message with all fields",
jsonInput: `{"from":"telegram","chatid":"123","userid":"456","message":"hello","plugin":"telegram","mode":"private","misc":{"answer":1,"bot_nick":"aleesa","csign":"!","fwd_cnt":1,"good_morning":0,"msg_format":1,"username":"user"}}`,
expectedFrom: "telegram",
expectedChatid: "123",
expectedPlugin: "telegram",
expectCsignNil: false,
},
{
name: "minimal message",
jsonInput: `{"from":"irc","chatid":"#room","userid":"nick","message":"!karma","plugin":"irc","mode":"channel"}`,
expectedFrom: "irc",
expectedChatid: "#room",
expectedPlugin: "irc",
expectCsignNil: true,
},
{
name: "message with fwd_cnt zero",
jsonInput: `{"from":"jabber","chatid":"room@conference","userid":"user","message":"test","plugin":"jabber","mode":"group","misc":{"fwd_cnt":0}}`,
expectedFrom: "jabber",
expectedChatid: "room@conference",
expectedPlugin: "jabber",
expectCsignNil: true,
},
{
name: "message with threadid",
jsonInput: `{"from":"telegram","chatid":"123","userid":"456","threadid":"789","message":"reply","plugin":"telegram","mode":"thread"}`,
expectedFrom: "telegram",
expectedChatid: "123",
expectedPlugin: "telegram",
expectCsignNil: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var msg rMsg
if err := json.Unmarshal([]byte(tt.jsonInput), &msg); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
if msg.From != tt.expectedFrom {
t.Errorf("From: expected %s, got %s", tt.expectedFrom, msg.From)
}
if msg.Chatid != tt.expectedChatid {
t.Errorf("Chatid: expected %s, got %s", tt.expectedChatid, msg.Chatid)
}
if msg.Plugin != tt.expectedPlugin {
t.Errorf("Plugin: expected %s, got %s", tt.expectedPlugin, msg.Plugin)
}
if tt.expectCsignNil && msg.Misc.Csign != "" {
t.Errorf("Csign: expected empty, got %s", msg.Misc.Csign)
}
})
}
}
func TestSMsgJSONSerialization(t *testing.T) {
tests := []struct {
name string
message sMsg
}{
{
name: "full message",
message: sMsg{
From: "telegram",
Chatid: "123",
Userid: "456",
Threadid: "789",
Message: "Hello world",
Plugin: "telegram",
Mode: "private",
Misc: struct {
Answer int64 `json:"answer"`
Botnick string `json:"bot_nick"`
Csign string `json:"csign"`
Fwdcnt int64 `json:"fwd_cnt"`
GoodMorning int64 `json:"good_morning"`
Msgformat int64 `json:"msg_format"`
Username string `json:"username"`
}{
Answer: 1,
Botnick: "aleesa",
Csign: "!",
Fwdcnt: 2,
GoodMorning: 0,
Msgformat: 1,
Username: "testuser",
},
},
},
{
name: "minimal message",
message: sMsg{
From: "irc",
Chatid: "#room",
Userid: "nick",
Message: "!help",
Plugin: "irc",
Mode: "channel",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := json.Marshal(tt.message)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
// Verify it's valid JSON
var result map[string]interface{}
if err := json.Unmarshal(data, &result); err != nil {
t.Fatalf("marshaled data is not valid JSON: %v", err)
}
// Verify key fields are present
if _, ok := result["from"]; !ok {
t.Error("missing 'from' field")
}
if _, ok := result["chatid"]; !ok {
t.Error("missing 'chatid' field")
}
if _, ok := result["message"]; !ok {
t.Error("missing 'message' field")
}
})
}
}
func TestRMsgToSMsgConversion(t *testing.T) {
fwdcnt := new(int64)
*fwdcnt = 3
input := rMsg{
From: "telegram",
Chatid: "123",
Userid: "456",
Threadid: "789",
Message: "!friday",
Plugin: "telegram",
Mode: "private",
Misc: struct {
Answer int64 `json:"answer,omitempty"`
Botnick string `json:"bot_nick,omitempty"`
Csign string `json:"csign,omitempty"`
Fwdcnt *int64 `json:"fwd_cnt,omitempty"`
GoodMorning int64 `json:"good_morning,omitempty"`
Msgformat int64 `json:"msg_format,omitempty"`
Username string `json:"username,omitempty"`
}{
Answer: 0,
Botnick: "aleesa",
Csign: "!",
Fwdcnt: fwdcnt,
GoodMorning: 1,
Msgformat: 1,
Username: "testuser",
},
}
// Convert
output := convertToSMsg(input)
// Verify all fields transferred
if output.From != input.From {
t.Errorf("From: expected %s, got %s", input.From, output.From)
}
if output.Chatid != input.Chatid {
t.Errorf("Chatid: expected %s, got %s", input.Chatid, output.Chatid)
}
if output.Userid != input.Userid {
t.Errorf("Userid: expected %s, got %s", input.Userid, output.Userid)
}
if output.Threadid != input.Threadid {
t.Errorf("Threadid: expected %s, got %s", input.Threadid, output.Threadid)
}
if output.Message != input.Message {
t.Errorf("Message: expected %s, got %s", input.Message, output.Message)
}
if output.Plugin != input.Plugin {
t.Errorf("Plugin: expected %s, got %s", input.Plugin, output.Plugin)
}
if output.Mode != input.Mode {
t.Errorf("Mode: expected %s, got %s", input.Mode, output.Mode)
}
if output.Misc.Csign != input.Misc.Csign {
t.Errorf("Misc.Csign: expected %s, got %s", input.Misc.Csign, output.Misc.Csign)
}
if output.Misc.Fwdcnt != *input.Misc.Fwdcnt {
t.Errorf("Misc.Fwdcnt: expected %d, got %d", *input.Misc.Fwdcnt, output.Misc.Fwdcnt)
}
if output.Misc.Username != input.Misc.Username {
t.Errorf("Misc.Username: expected %s, got %s", input.Misc.Username, output.Misc.Username)
}
if output.Misc.Answer != input.Misc.Answer {
t.Errorf("Misc.Answer: expected %d, got %d", input.Misc.Answer, output.Misc.Answer)
}
if output.Misc.Botnick != input.Misc.Botnick {
t.Errorf("Misc.Botnick: expected %s, got %s", input.Misc.Botnick, output.Misc.Botnick)
}
if output.Misc.Msgformat != input.Misc.Msgformat {
t.Errorf("Misc.Msgformat: expected %d, got %d", input.Misc.Msgformat, output.Misc.Msgformat)
}
if output.Misc.GoodMorning != input.Misc.GoodMorning {
t.Errorf("Misc.GoodMorning: expected %d, got %d", input.Misc.GoodMorning, output.Misc.GoodMorning)
}
}
// convertToSMsg simulates the conversion logic from msgParser.go
func convertToSMsg(j rMsg) sMsg {
var message sMsg
message.From = j.From
message.Userid = j.Userid
message.Chatid = j.Chatid
message.Threadid = j.Threadid
message.Message = j.Message
message.Plugin = j.Plugin
message.Mode = j.Mode
message.Misc.Fwdcnt = *j.Misc.Fwdcnt
message.Misc.Csign = j.Misc.Csign
message.Misc.Username = j.Misc.Username
message.Misc.Answer = j.Misc.Answer
message.Misc.Botnick = j.Misc.Botnick
message.Misc.Msgformat = j.Misc.Msgformat
message.Misc.GoodMorning = j.Misc.GoodMorning
return message
}
func TestJSONFieldNaming(t *testing.T) {
// Test that sMsg uses lowercase "misc" in JSON
msg := sMsg{
From: "test",
Chatid: "1",
Userid: "2",
Message: "hello",
Plugin: "test",
Mode: "test",
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
// Check for lowercase "misc"
if !contains(string(data), `"misc"`) {
t.Errorf("expected lowercase 'misc' in JSON, got: %s", string(data))
}
// Ensure it doesn't contain capitalized "Misc"
if contains(string(data), `"Misc"`) {
t.Errorf("should not contain capitalized 'Misc' in JSON, got: %s", string(data))
}
}
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsAt(s, substr))
}
func containsAt(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
func TestRMsgFwdcntPointer(t *testing.T) {
// Test that Fwdcnt as pointer works correctly with JSON
jsonInput := `{"from":"test","chatid":"1","userid":"1","message":"test","plugin":"test","mode":"test","misc":{"fwd_cnt":5}}`
var msg rMsg
if err := json.Unmarshal([]byte(jsonInput), &msg); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
if msg.Misc.Fwdcnt == nil {
t.Fatal("Fwdcnt should not be nil")
}
if *msg.Misc.Fwdcnt != 5 {
t.Errorf("expected Fwdcnt=5, got %d", *msg.Misc.Fwdcnt)
}
}
func TestRMsgFwdcntNilWhenOmitted(t *testing.T) {
jsonInput := `{"from":"test","chatid":"1","userid":"1","message":"test","plugin":"test","mode":"test"}`
var msg rMsg
if err := json.Unmarshal([]byte(jsonInput), &msg); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
if msg.Misc.Fwdcnt != nil {
t.Errorf("Fwdcnt should be nil when omitted, got %d", *msg.Misc.Fwdcnt)
}
}
func TestMyConfigForwardChannels(t *testing.T) {
cfg := myConfig{
Server: "localhost",
Port: 6379,
Channel: "test",
Csign: "!",
}
// Test that struct can hold custom values
cfg.ForwardChannels.Games = "custom-games"
cfg.ForwardChannels.Phrases = "custom-phrases"
cfg.ForwardChannels.Webapp = "custom-webapp"
cfg.ForwardChannels.WebappGo = "custom-webapp-go"
cfg.ForwardChannels.Craniac = "custom-craniac"
if cfg.ForwardChannels.Games != "custom-games" {
t.Errorf("Games: expected custom-games, got %s", cfg.ForwardChannels.Games)
}
if cfg.ForwardChannels.Phrases != "custom-phrases" {
t.Errorf("Phrases: expected custom-phrases, got %s", cfg.ForwardChannels.Phrases)
}
if cfg.ForwardChannels.Webapp != "custom-webapp" {
t.Errorf("Webapp: expected custom-webapp, got %s", cfg.ForwardChannels.Webapp)
}
if cfg.ForwardChannels.WebappGo != "custom-webapp-go" {
t.Errorf("WebappGo: expected custom-webapp-go, got %s", cfg.ForwardChannels.WebappGo)
}
if cfg.ForwardChannels.Craniac != "custom-craniac" {
t.Errorf("Craniac: expected custom-craniac, got %s", cfg.ForwardChannels.Craniac)
}
}