Files
zeekayandGitHub babbdec4b8 debrand: SigNoz -> O11y across the tree (+ otel-collector v0.144.7, schema cutover migration) (#28)
* debrand: signoz/SigNoz/SIGNOZ -> o11y/O11y/O11Y across the tree

Rename all SigNoz *branding* to O11y in file contents, file/dir names,
package names, env vars, config keys, comments, docs, SDK code, and the
ClickHouse schema identifiers the querier reads.

Collector dependency:
- Repoint github.com/hanzoai/signoz-otel-collector ->
  github.com/hanzoai/otel-collector and bump v0.144.6 -> v0.144.7
  (go.mod + go.sum reconciled via go mod tidy). Internal package paths
  also debranded upstream: signozschemamigrator -> o11yschemamigrator,
  signozlogspipelineprocessor -> o11ylogspipelineprocessor.
- Fix: v0.144.7 dropped otelconst.DistributedFieldKeysTable from the
  collector's public constants; pin FieldKeysTable = "distributed_field_keys"
  locally in pkg/telemetrymetadata/tables.go.

Package/dir renames (package decl + all importers):
  pkg/apiserver/signozapiserver -> o11yapiserver, plus signozalertmanager,
  signozauthzapi, signozglobal, signozquerier, signozruler, and pkg/signoz -> pkg/o11y.

Schema (read plane): signoz_traces/metrics/logs/metadata/analytics/meter and
signoz_index_v3/index_v2/error_index_v2/spans (+ distributed_*) -> o11y_*.
Data-preserving cutover migration added:
  deploy/clickhouse/migrations/0001_rename_signoz_to_o11y.sql
  (metadata-only RENAME DATABASE/TABLE, no drop/recreate).

Preserved (attribution / external / wire contracts):
- LICENSE + NOTICE verbatim (incl. "software from SigNoz", "Copyright ... SigNoz Inc.").
- Upstream github.com/SigNoz/* repo URLs + @SigNoz/* CODEOWNERS teams.
- Billing resource-attribute regex "signoz.workspace.*" (external wire contract).

go build ./pkg/... ./cmd/... = 0; gofmt clean; renamed+schema pkg tests pass.

* o11y: bump collector to v0.144.8 (o11y_* physical schema writer) + lockstep deploy plan

Collector v0.144.8's schema-migrator + exporters now CREATE/WRITE the same
o11y_* physical databases/tables the querier reads and the RENAME migration
(0001_rename_signoz_to_o11y.sql) targets.

Cross-checked byte-identical: writer DB set == migration target DB set (6);
writer prefixed-table set == migration target table set (8); o11y querier
reads and cloud reads are subsets of that set. Zero table-name mismatches
between writer, readers, and migration.

Collector go.mod unchanged between v0.144.7 and v0.144.8 (identical go.mod
hash) — pure source rename, no module-graph change.

Adds deploy/clickhouse/DEPLOY_signoz_to_o11y_cutover.md: the lockstep order
(collector v0.144.8 -> RENAME migration -> o11y+cloud readers) with a
fresh/scratch-ClickHouse pre-flight (create via migrator, emit trace+metric,
query back) and rollback. Ships together or telemetry blackholes.

go build ./pkg/... ./cmd/community = 0.
2026-07-09 13:08:55 -07:00

202 lines
7.1 KiB
Go

package errors
import (
"context"
"errors" //nolint:depguard
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNew(t *testing.T) {
typ := typ{"test-error"}
err := New(typ, MustNewCode("code"), "test error info")
assert.NotNil(t, err)
}
func TestNewf(t *testing.T) {
typ := typ{"test-error"}
err := Newf(typ, MustNewCode("test_code"), "test error info with %s", "string")
assert.NotNil(t, err)
assert.Equal(t, "test error info with string", err.Error())
}
func TestWrapf(t *testing.T) {
typ := typ{"test-error"}
err := Wrapf(errors.New("original error"), typ, MustNewCode("test_code"), "info for err %d", 2)
assert.NotNil(t, err)
}
func TestError(t *testing.T) {
typ := typ{"test-error"}
err1 := New(typ, MustNewCode("test_code"), "info for err1")
assert.Equal(t, "info for err1", err1.Error())
err2 := Wrapf(err1, typ, MustNewCode("test_code"), "info for err2")
assert.Equal(t, "info for err1", err2.Error())
}
func TestUnwrapb(t *testing.T) {
typ := typ{"test-error"}
oerr := errors.New("original error")
berr := Wrapf(oerr, typ, MustNewCode("test_code"), "this is a base err").WithUrl("https://docs").WithAdditional("additional err")
atyp, acode, amessage, aerr, au, aa := Unwrapb(berr)
assert.Equal(t, typ, atyp)
assert.Equal(t, "test_code", acode.String())
assert.Equal(t, "this is a base err", amessage)
assert.Equal(t, oerr, aerr)
assert.Equal(t, "https://docs", au)
assert.Equal(t, []additional{{message: "additional err"}}, aa)
atyp, _, _, _, _, _ = Unwrapb(oerr)
assert.Equal(t, TypeInternal, atyp)
}
func TestAttr(t *testing.T) {
err := New(TypeInternal, MustNewCode("test_code"), "test error")
attr := Attr(err)
assert.Equal(t, "exception", attr.Key)
assert.Equal(t, err, attr.Value.Any())
}
func TestWithSuggestions(t *testing.T) {
err := New(TypeInternal, MustNewCode("test_code"), "test error").WithSuggestions("try this")
assert.Equal(t, []string{"try this"}, suggestionsOf(err))
// WithSuggestions replaces the existing list.
err = err.WithSuggestions("try this instead")
assert.Equal(t, []string{"try this instead"}, suggestionsOf(err))
// Variadic form replaces with multiple entries.
err = err.WithSuggestions("first", "second")
assert.Equal(t, []string{"first", "second"}, suggestionsOf(err))
}
func TestWithSuggestiveAdditional(t *testing.T) {
// WithSuggestiveAdditional attaches suggestions to a specific detail (in the
// errors array), distinct from the error-wide WithSuggestions.
err := NewInvalidInputf(MustNewCode("bad_field"), "unknown field %q", "filed").
WithSuggestiveAdditional("field `filed` not found", "did you mean: `field`")
j := AsJSON(err)
assert.Equal(t, []responseerroradditional{
{Message: "field `filed` not found", Suggestions: []string{"did you mean: `field`"}},
}, j.Errors)
assert.Empty(t, j.Suggestions, "detail-scoped suggestions must not leak into the error-wide list")
}
func TestWithRetryAfter(t *testing.T) {
err := New(TypeInternal, MustNewCode("test_code"), "test error").WithRetryAfter(5 * time.Microsecond)
r := retryOf(err)
assert.Equal(t, 5, int(r.delay.Microseconds()))
}
func TestAsJSONBaseError(t *testing.T) {
err := New(TypeInvalidInput, MustNewCode("bad_input"), "field foo is bad").
WithUrl("https://docs/bad_input").
WithAdditional("hint1", "hint2").
WithSuggestions("try this")
j := AsJSON(err)
assert.Equal(t, "invalid-input", j.Type)
assert.Equal(t, "bad_input", j.Code)
assert.Equal(t, "field foo is bad", j.Message)
assert.Equal(t, "https://docs/bad_input", j.Url)
// A detail with no suggestions carries an empty (non-nil) slice — the
// suggestions field is non-nullable, so it marshals to [] rather than null.
assert.Equal(t, []responseerroradditional{
{Message: "hint1", Suggestions: []string{}},
{Message: "hint2", Suggestions: []string{}},
}, j.Errors)
// InvalidInput auto-applies the after_fix policy via NewInvalidInputf — but
// New (bare constructor) does not. The retry block should reflect that.
assert.Nil(t, j.Retry, "bare New(...) should not populate a retry block")
assert.Equal(t, []string{"try this"}, j.Suggestions)
}
func TestAsJSONWrappedErrorPreservesHints(t *testing.T) {
// An inner base carries the user-facing hints (e.g. produced inside an
// UnmarshalJSON), then gets re-wrapped (e.g. WrapInvalidInputf). suggestionsOf
// must walk the cause chain so the hints still surface.
inner := NewInvalidInputf(MustNewCode("bad_kind"), "unknown panel kind %q", "boom").
WithSuggestions("valid references: a, b, c")
wrapped := WrapInvalidInputf(inner, MustNewCode("outer"), "%s", inner.Error())
j := AsJSON(wrapped)
assert.Equal(t, []string{"valid references: a, b, c"}, j.Suggestions,
"suggestions on an inner base must survive wrapping")
}
func TestAsJSONRetryBlock(t *testing.T) {
t.Run("RetryAfterIncludesDuration", func(t *testing.T) {
err := NewTimeoutf(MustNewCode("slow"), "slow").WithRetryAfter(5 * time.Second)
j := AsJSON(err)
require.NotNil(t, j.Retry)
assert.Equal(t, 5*time.Second, j.Retry.Delay)
})
t.Run("NonAfterPolicyOmitsDurationField", func(t *testing.T) {
// NewInvalidInputf auto-applies retryAfterFix via the constructor helper.
err := NewInvalidInputf(MustNewCode("bad"), "bad")
j := AsJSON(err)
require.Nil(t, j.Retry, "retry must be empty")
})
t.Run("BareErrorOmitsRetryBlock", func(t *testing.T) {
err := New(TypeInternal, MustNewCode("boom"), "boom")
j := AsJSON(err)
assert.Nil(t, j.Retry, "bare New(...) without WithRetry* must omit retry")
})
t.Run("NonBaseErrorOmitsRetryBlock", func(t *testing.T) {
// Stdlib errors carry no retry metadata; AsJSON omits the retry block.
j := AsJSON(errors.New("plain stdlib error"))
assert.Nil(t, j.Retry, "non-base errors must omit the retry block")
})
}
func TestAsJSONEmptyWhenNoneSet(t *testing.T) {
// errors and suggestions are non-nullable in the OpenAPI spec, so AsJSON
// leaves them as empty (non-nil) slices when the error carries none — they
// marshal to [] rather than null.
j := AsJSON(New(TypeInternal, MustNewCode("boom"), "boom"))
assert.NotNil(t, j.Suggestions)
assert.Empty(t, j.Suggestions)
assert.NotNil(t, j.Errors)
assert.Empty(t, j.Errors)
}
func TestWithStacktrace(t *testing.T) {
err := New(TypeInternal, MustNewCode("test_code"), "panic").WithStacktrace("custom stack trace")
assert.Equal(t, "custom stack trace", err.Stacktrace())
assert.Equal(t, "panic", err.Error())
typ, code, message, _, _, _ := Unwrapb(err)
assert.Equal(t, TypeInternal, typ)
assert.Equal(t, "test_code", code.String())
assert.Equal(t, "panic", message)
}
// Wrapped context sentinels must remain detectable via errors.Is so callers
// that branch on context.Canceled / context.DeadlineExceeded keep working
// after the error passes through one of the o11y Wrap* helpers.
func TestWrapPreservesContextSentinels(t *testing.T) {
canceled := WrapCanceledf(context.Canceled, MustNewCode("canceled"), "op canceled")
assert.True(t, Is(canceled, context.Canceled))
assert.False(t, Is(canceled, context.DeadlineExceeded))
deadline := WrapTimeoutf(context.DeadlineExceeded, MustNewCode("timeout"), "op timed out")
assert.True(t, Is(deadline, context.DeadlineExceeded))
assert.False(t, Is(deadline, context.Canceled))
}