Files
zeekay f015b4f8a4 debrand: drop signoz — o11y only across collector code
Finish the signoz -> o11y debrand that the debrand branch left partial:
- repoint SigNoz/signoz-otel-collector/{utils,constants,keycheck,metadataexporter,
  processor/*} imports to the local hanzoai/otel-collector/* copies (already folded)
- rename package signozllmpricingprocessor -> o11yllmpricingprocessor (dir+pkg+typeStr)
- rename component type IDs: signozclickhouse{metrics,meter} -> o11yclickhouse*,
  signozlogspipeline -> o11ylogspipeline, signozllmpricing -> o11yllmpricing
- rename files: schema-signoz.go -> schema.go, signoz_attributes.go -> o11y_attributes.go
- vendor SigNoz/grok's mutex-patched fork in-tree as pkg/grok (Apache-2.0, self-contained)
  and drop the segmentio/ksuid->signoz/ksuid + vjeantet/grok->signoz/grok replaces
- restore constants.DistributedFieldKeysTable dropped in the earlier fold
- go.mod/go.sum: SigNoz/signoz-otel-collector require gone; 0 signoz in code

Full ./... build passes. LICENSE keeps upstream Apache attribution (legal).
Residual: ClickHouse dd-sketch driver forks (ch-go, clickhouse-go/v2) — next.
2026-07-12 18:04:52 -07:00

135 lines
3.9 KiB
Go

package o11yllmpricingprocessor
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap/confmaptest"
)
var processorType = component.MustNewType(typeStr)
func loadConfig(t *testing.T, id component.ID) (*Config, error) {
t.Helper()
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
sub, err := cm.Sub(id.String())
require.NoError(t, err)
if err := sub.Unmarshal(cfg); err != nil {
return nil, err
}
return cfg.(*Config), nil
}
func TestLoadConfig(t *testing.T) {
t.Parallel()
t.Run("full", func(t *testing.T) {
cfg, err := loadConfig(t, component.NewID(processorType))
require.NoError(t, err)
require.NoError(t, cfg.Validate())
assert.Equal(t, "gen_ai.request.model", cfg.Attrs.Model)
assert.Equal(t, "gen_ai.usage.input_tokens", cfg.Attrs.In)
assert.Equal(t, "gen_ai.usage.output_tokens", cfg.Attrs.Out)
assert.Equal(t, "gen_ai.usage.input_token_details.cached", cfg.Attrs.CacheRead)
assert.Equal(t, "gen_ai.usage.input_token_details.cache_creation", cfg.Attrs.CacheWrite)
require.Len(t, cfg.DefaultPricing.Rules, 2)
gpt := cfg.DefaultPricing.Rules[0]
assert.Equal(t, "gpt-4o", gpt.Name)
assert.Equal(t, []string{"gpt-4o*"}, gpt.Pattern)
assert.Equal(t, CacheModeSubtract, gpt.Cache.Mode)
assert.Equal(t, 5.0, gpt.In)
assert.Equal(t, 15.0, gpt.Out)
assert.Equal(t, 2.5, gpt.Cache.Read)
assert.Equal(t, 0.0, gpt.Cache.Write)
claude := cfg.DefaultPricing.Rules[1]
assert.Equal(t, "claude", claude.Name)
assert.Equal(t, []string{"claude-*"}, claude.Pattern)
assert.Equal(t, CacheModeAdditive, claude.Cache.Mode)
assert.Equal(t, 3.0, claude.In)
assert.Equal(t, 15.0, claude.Out)
assert.Equal(t, 0.30, claude.Cache.Read)
assert.Equal(t, 3.75, claude.Cache.Write)
assert.Equal(t, "_o11y.gen_ai.cost_input", cfg.OutputAttrs.In)
assert.Equal(t, "_o11y.gen_ai.cost_output", cfg.OutputAttrs.Out)
assert.Equal(t, "_o11y.gen_ai.cost_cache_read", cfg.OutputAttrs.CacheRead)
assert.Equal(t, "_o11y.gen_ai.cost_cache_write", cfg.OutputAttrs.CacheWrite)
assert.Equal(t, "_o11y.gen_ai.total_cost", cfg.OutputAttrs.Total)
})
t.Run("minimal", func(t *testing.T) {
cfg, err := loadConfig(t, component.NewIDWithName(processorType, "minimal"))
require.NoError(t, err)
require.NoError(t, cfg.Validate())
// Optional output attrs are empty — only total is required.
assert.Empty(t, cfg.OutputAttrs.In)
assert.Empty(t, cfg.OutputAttrs.CacheRead)
assert.Equal(t, "_o11y.gen_ai.total_cost", cfg.OutputAttrs.Total)
})
}
func TestValidateErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
id component.ID
errContains string
}{
{
name: "no_model_attr",
id: component.NewIDWithName(processorType, "no_model_attr"),
errContains: "attrs.model must not be empty",
},
{
name: "no_pattern",
id: component.NewIDWithName(processorType, "no_pattern"),
errContains: "pattern must not be empty",
},
{
name: "bad_cache_mode",
id: component.NewIDWithName(processorType, "bad_cache_mode"),
errContains: "cache.mode must be",
},
{
name: "no_total_output",
id: component.NewIDWithName(processorType, "no_total_output"),
errContains: "output_attrs.total must not be empty",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cfg, err := loadConfig(t, tc.id)
require.NoError(t, err)
err = cfg.Validate()
require.Error(t, err)
assert.ErrorContains(t, err, tc.errContains)
})
}
}
func TestDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
require.NotNil(t, cfg)
_, ok := cfg.(*Config)
require.True(t, ok)
}