@@ -4,24 +4,26 @@ package sql_exporter
44
55import (
66 "fmt"
7+ "io"
8+ "log/slog"
79 _ "net/http/pprof"
810 "os"
911 "path/filepath"
1012 "runtime"
11- "strings"
1213 "testing"
1314
1415 _ "github.com/mithrandie/csvq-driver"
16+ "go.yaml.in/yaml/v3"
1517
1618 "github.com/prometheus/client_golang/prometheus"
1719)
1820
19- // setupCSVDir creates a temp directory with a minimal CSV file usable as a table.
21+ // setupCSVDirs creates a temp directory with a minimal CSV file usable as a table.
2022func setupCSVDirs (t * testing.T , n int ) []string {
2123 t .Helper ()
2224 base := t .TempDir ()
2325 dirs := make ([]string , n )
24- for i := range n {
26+ for i := range dirs {
2527 dir := filepath .Join (base , fmt .Sprintf ("csv_%d" , i ))
2628 if err := os .MkdirAll (dir , 0o755 ); err != nil {
2729 t .Fatalf ("mkdir CSV dir %d: %v" , i , err )
@@ -34,55 +36,86 @@ func setupCSVDirs(t *testing.T, n int) []string {
3436 return dirs
3537}
3638
37- // writeConfig writes a sql_exporter YAML config file pointing at csvDir with
38- // n targets, returning the config file path.
39- func writeConfig (t * testing.T , dirs []string , n int ) string {
39+ func writeConfig (t * testing.T , dirs []string ) string {
4040 t .Helper ()
4141
42- var sb strings.Builder
43- for i := range n {
44- fmt .Fprintf (& sb , `
45- - collector_name: col%d
46- metrics:
47- - metric_name: csvq_value_%d
48- type: gauge
49- help: "test metric %d"
50- values: [value]
51- query: "SELECT value FROM metrics"
52- ` , i , i , i )
42+ type metric struct {
43+ Name string `yaml:"metric_name"`
44+ Type string `yaml:"type"`
45+ Help string `yaml:"help"`
46+ Values []string `yaml:"values"`
47+ Query string `yaml:"query"`
5348 }
54- collectors := sb .String ()
55-
56- sb .Reset ()
57- for i := range n {
58- fmt .Fprintf (& sb , " target%d: csvq:%s\n " , i , dirs [i ])
49+ type collector struct {
50+ Name string `yaml:"collector_name"`
51+ Metrics []metric `yaml:"metrics"`
52+ }
53+ type staticConfig struct {
54+ Targets map [string ]string `yaml:"targets"`
55+ }
56+ type job struct {
57+ Name string `yaml:"job_name"`
58+ Collectors []string `yaml:"collectors"`
59+ StaticConfigs []staticConfig `yaml:"static_configs"`
60+ }
61+ type global struct {
62+ ScrapeTimeout string `yaml:"scrape_timeout"`
63+ ScrapeTimeoutOffset string `yaml:"scrape_timeout_offset"`
64+ MinInterval string `yaml:"min_interval"`
65+ MaxConnections int `yaml:"max_connections"`
66+ MaxIdleConnections int `yaml:"max_idle_connections"`
67+ }
68+ type cfg struct {
69+ Global global `yaml:"global"`
70+ Collectors []collector `yaml:"collectors"`
71+ Jobs []job `yaml:"jobs"`
5972 }
60- targets := sb .String ()
61-
62- content := fmt .Sprintf (`
63- global:
64- scrape_timeout: 10s
65- scrape_timeout_offset: 500ms
66- min_interval: 0s
67- max_connections: 3
68- max_idle_connections: 3
69-
70- collector_files: []
7173
72- collectors:%s
74+ n := len (dirs )
75+ collectors := make ([]collector , n )
76+ collectorNames := make ([]string , n )
77+ targets := make (map [string ]string , n )
78+
79+ for i := range dirs {
80+ name := fmt .Sprintf ("col%d" , i )
81+ collectorNames [i ] = name
82+ collectors [i ] = collector {
83+ Name : name ,
84+ Metrics : []metric {{
85+ Name : fmt .Sprintf ("csvq_value_%d" , i ),
86+ Type : "gauge" ,
87+ Help : fmt .Sprintf ("test metric %d" , i ),
88+ Values : []string {"value" },
89+ Query : "SELECT value FROM metrics" ,
90+ }},
91+ }
92+ targets [fmt .Sprintf ("target%d" , i )] = "csvq:" + dirs [i ]
93+ }
7394
74- jobs:
75- - job_name: test_job
76- collectors: [col0, col1, col2, col3, col4, col5, col6, col7, col8, col9]
77- static_configs:
78- - targets:
79- %s` , collectors , targets )
95+ c := cfg {
96+ Global : global {
97+ ScrapeTimeout : "10s" ,
98+ ScrapeTimeoutOffset : "500ms" ,
99+ MinInterval : "0s" ,
100+ MaxConnections : 3 ,
101+ MaxIdleConnections : 3 ,
102+ },
103+ Collectors : collectors ,
104+ Jobs : []job {{
105+ Name : "test_job" ,
106+ Collectors : collectorNames ,
107+ StaticConfigs : []staticConfig {{Targets : targets }},
108+ }},
109+ }
80110
111+ data , err := yaml .Marshal (c )
112+ if err != nil {
113+ t .Fatalf ("marshal config: %v" , err )
114+ }
81115 cfgFile := filepath .Join (t .TempDir (), "sql_exporter.yml" )
82- if err := os .WriteFile (cfgFile , [] byte ( content ) , 0o644 ); err != nil {
116+ if err := os .WriteFile (cfgFile , data , 0o644 ); err != nil {
83117 t .Fatalf ("write config: %v" , err )
84118 }
85-
86119 return cfgFile
87120}
88121
@@ -132,7 +165,7 @@ func TestReloadMemoryLeak(t *testing.T) {
132165 )
133166
134167 dirs := setupCSVDirs (t , numTargets )
135- cfgFile := writeConfig (t , dirs , numTargets )
168+ cfgFile := writeConfig (t , dirs )
136169
137170 e , err := NewExporter (cfgFile , prometheus .NewRegistry ())
138171 if err != nil {
@@ -143,6 +176,11 @@ func TestReloadMemoryLeak(t *testing.T) {
143176
144177 t .Logf ("goroutine delta=%d (expected <= %d)" , delta , tolerance )
145178 if delta > tolerance {
146- t .Errorf ("expected goroutine delta <= %d, got %d — leak still present " , tolerance , delta )
179+ t .Errorf ("expected goroutine delta <= %d, got %d — leak suspected " , tolerance , delta )
147180 }
148181}
182+
183+ func TestMain (m * testing.M ) {
184+ slog .SetDefault (slog .New (slog .NewTextHandler (io .Discard , nil )))
185+ os .Exit (m .Run ())
186+ }
0 commit comments