Skip to content

Latest commit

 

History

History
162 lines (124 loc) · 5.15 KB

File metadata and controls

162 lines (124 loc) · 5.15 KB

observer-go

Minimal Go-facing provider micro-library for Observer.

If you are new to this surface, start with HOWTO.md before reading the individual snippets and starters.

This aims at the same DX bar as the C, Rust, TypeScript, and Python libraries:

  • authors write Describe(...), Test(...), It(...), and Expect(...)
  • default identity is derived deterministically from explicit suite path plus test title
  • optional WithID(...) provides a refactor-stable override when needed
  • observation is bounded and opt-in through ctx.Observe().*
  • direct host and embedded observe dispatch are owned by the library

The common path stays human-first. The deterministic boundary stays explicit.

Files

  • HOWTO.md: detailed user manual covering authoring, determinism, host transport, inventory derivation, and end-to-end workflow
  • observer.go: public API
  • observer_test.go: stdlib-only self-test coverage for the Go SDK
  • go.mod: installable Go module metadata for the standalone Go library
  • examples/example-smoke/: tiny collection and execution example
  • examples/host-example/: tiny direct list and run host example
  • examples/host-embed-example/: own-main style observe namespace example
  • starter/: runnable project-shaped example with Go build, provider host build, inventory derivation, suite run, and snapshot verification
  • starter-embedded/: runnable app-shaped example where the application keeps main() and routes observe ... through its own CLI
  • starter-embedded-failure/: runnable failing companion for the embedded app-shaped path
  • starter-failure/: runnable failing companion showing the same provider flow with one intentionally failing exported test

If you want the full end-to-end workflow rather than isolated snippets, start with starter/, then read starter-embedded/, and then compare those with starter-failure/ and starter-embedded-failure/.

Minimal Shape

package main

import observer "github.com/frogfish/observer/lib/go/observer"

func main() {
	tests := observer.MustCollectTests(func() {
		observer.Describe("database", func() {
			observer.Test("access to the database", func(ctx *observer.TestContext) {
				ctx.Stdout("ok\n")
				observer.Expect(true).ToBeTruthy()
			})
		})
	})

	_ = tests
}

When WithID(...) is omitted, Observer derives a deterministic identity from suite path, test title, and duplicate occurrence order.

If a test wants a refactor-stable identity, it opts into WithID(...) explicitly:

observer.Test("access to the database", func(ctx *observer.TestContext) {
	observer.Expect(true).ToBeTruthy()
}, observer.WithID("database/access"))

If a test wants to emit observational data, it uses the author context directly:

observer.Test("access to the database", func(ctx *observer.TestContext) {
	observe := ctx.Observe()
	_ = observe.Metric("wall_time_ns", 104233.0)
	_ = observe.Vector("request_latency_ns", []float64{1000.0, 1100.0, 980.0})
	_ = observe.Tag("resource_path", "fixtures/config.json")
	observer.Expect(true).ToBeTruthy()
}, observer.WithID("database/access"))

Validation Rules

  • explicit WithID(...), when present, must be non-empty
  • resolved canonical identities must be unique
  • resolved targets must be unique
  • deterministic sorting is by canonical name, then target

In this first cut, the resolved identity is used for both canonical name and target.

Test

cd lib/go
go test ./...

Smoke Example

cd lib/go
go run ./examples/example-smoke

Host Example

cd lib/go
go run ./examples/host-example -- list
go run ./examples/host-example -- observe --target pkg::smoke --timeout-ms 1000
go run ./examples/host-example -- run --target pkg::fail --timeout-ms 1000

The library owns the standard provider host transport for Go too. A direct host can stay nearly trivial:

tests := observer.MustCollectTests(func() {
	observer.Describe("pkg", func() {
		observer.Test("smoke test", func(ctx *observer.TestContext) {
			ctx.Stdout("ok\n")
			observer.Expect(true).ToBeTruthy()
		}, observer.WithID("pkg::smoke"))
	})
})

if err := observer.ObserverHostMain("go", tests); err != nil {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(2)
}

For developer-facing usage, prefer observe. run remains available for compatibility with the standardized outer provider contract.

Own Main Integration

If a project already owns its CLI, the library can also serve an embedded observe namespace:

func main() {
	tests := observer.MustCollectTests(func() {
		observer.Describe("pkg", func() {
			observer.Test("embedded smoke test", func(ctx *observer.TestContext) {
				ctx.Stdout("ok\n")
				observer.Expect(true).ToBeTruthy()
			}, observer.WithID("pkg::embedded-smoke"))
		})
	})

	if len(os.Args) > 1 && os.Args[1] == "observe" {
		if err := observer.ObserverHostDispatchEmbedded("go", "observe", tests, os.Args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
		return
	}

	appMain(os.Args)
}

Compile and run that path with:

cd lib/go
go run ./examples/host-embed-example -- observe list
go run ./examples/host-embed-example -- observe --target pkg::embedded-smoke --timeout-ms 1000
go run ./examples/host-embed-example