- docs/backend: admin controllers, forms, lists and filters, relation manager, users and permissions, settings, partials and widgets, admin SPA - docs/services: storage, outbound HTTP, realtime, Web Push, search, parity testing and the Frontend and AJAX (not provided) page - Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse and beachcomber TestDocs* regions run on their Postgres harnesses - concept map rows link their guide pages and the not-provided rows the Frontend and AJAX page; index lists Backend, Database and Services - TestDocsRequiredPages asserts the D-08 section order
70 lines
2.0 KiB
Go
70 lines
2.0 KiB
Go
package tide_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/tide"
|
|
)
|
|
|
|
// backend stands in for one implementation of GET /api/blog/posts.
|
|
func backend(body string) *httptest.Server {
|
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
fmt.Fprint(w, body)
|
|
}))
|
|
}
|
|
|
|
func ExampleReplayFlow() {
|
|
ctx := context.Background()
|
|
// The reference (PHP) backend and two Go ports. IDs and *_at timestamps
|
|
// legitimately differ; the second port changed a title.
|
|
reference := backend(`{"data":[{"id":12,"title":"Hello","created_at":"2026-09-30T10:00:00+00:00"}]}`)
|
|
defer reference.Close()
|
|
port := backend(`{"data":[{"id":3,"title":"Hello","created_at":"2026-10-01T08:30:00+00:00"}]}`)
|
|
defer port.Close()
|
|
broken := backend(`{"data":[{"id":3,"title":"hello","created_at":"2026-10-01T08:30:00+00:00"}]}`)
|
|
defer broken.Close()
|
|
|
|
raw, err := os.ReadFile("testdata/docs/posts-spec.yaml")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
spec, err := tide.ParseFlow(raw)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
// Record the reference once; the flow is what testdata/parity keeps.
|
|
flow, err := tide.RecordFlow(ctx, spec, tide.RecordConfig{Target: reference.URL})
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
for _, target := range []string{port.URL, broken.URL} {
|
|
res, err := tide.ReplayFlow(ctx, flow, tide.ReplayConfig{Target: target})
|
|
var mismatch *tide.MismatchError
|
|
if errors.As(err, &mismatch) {
|
|
res = mismatch.Result // a difference is an error carrying the result
|
|
} else if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Println("ok:", res.OK)
|
|
for _, step := range res.Steps {
|
|
for _, d := range step.Diffs {
|
|
fmt.Printf(" %s %s: want %s, got %s\n", step.ID, d.Path, d.Expected, d.Actual)
|
|
}
|
|
}
|
|
}
|
|
// Output:
|
|
// ok: true
|
|
// ok: false
|
|
// list-posts $.data[0].title: want "Hello", got "hello"
|
|
}
|