This is an automated email from the ASF dual-hosted git repository.
hanahmily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git
The following commit(s) were added to refs/heads/main by this push:
new 349a41316 fix(test/cases/measure): preserve Apache license header on
captured want fixtures and gate the capture loop (#1137)
349a41316 is described below
commit 349a4131690de0bff7bebc81d94cb260f240caf4
Author: Gao Hongtao <[email protected]>
AuthorDate: Thu May 21 21:28:43 2026 +0800
fix(test/cases/measure): preserve Apache license header on captured want
fixtures and gate the capture loop (#1137)
---
.gitignore | 1 +
Makefile | 5 ++-
test/cases/measure/cmd/capture/capture_test.go | 27 ++++++++++++++--
test/cases/measure/cmd/generate/capture.go | 9 +++++-
test/cases/measure/cmd/generate/types.go | 18 ++---------
test/cases/measure/data/license_header.go | 43 ++++++++++++++++++++++++++
6 files changed, 83 insertions(+), 20 deletions(-)
diff --git a/.gitignore b/.gitignore
index 336e73309..ca1ab487a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ gomock_reflect*
# Claude
.claude/
.omc/
+.omx/
# eBPF generated files and binaries
fodc/agent/internal/ktm/iomonitor/ebpf/generated/vmlinux.h
diff --git a/Makefile b/Makefile
index cd62b858f..806d24366 100644
--- a/Makefile
+++ b/Makefile
@@ -50,6 +50,9 @@ generate: default ## Generate API codes
generate-test-cases: ## Regenerate measure query test cases (input/*.ql,
input/*.yaml)
go run ./test/cases/measure/cmd/generate generate
test/cases/measure/data
+capture-test-cases: ## Capture measure query want fixtures (data/want/*.yaml)
by running queries against a standalone server
+ CAPTURE_WANT_FIXTURES=1 go test -count=1 -timeout 5m -run TestCapture
./test/cases/measure/cmd/capture/
+
build: TARGET=all
build: default ## Build all projects
@@ -270,7 +273,7 @@ release-push-candidate: ## Push release candidate
${PUSH_RELEASE_SCRIPTS}
.PHONY: all $(PROJECTS) clean build default nuke
-.PHONY: lint check tidy format pre-push generate-test-cases
check-import-boundaries
+.PHONY: lint check tidy format pre-push generate-test-cases capture-test-cases
check-import-boundaries
.PHONY: test test-race test-coverage test-ci test-docker
.PHONY: license-check license-fix license-dep
.PHONY: release release-binary release-source release-sign release-assembly
diff --git a/test/cases/measure/cmd/capture/capture_test.go
b/test/cases/measure/cmd/capture/capture_test.go
index d5d80a85d..a7c36241d 100644
--- a/test/cases/measure/cmd/capture/capture_test.go
+++ b/test/cases/measure/cmd/capture/capture_test.go
@@ -18,9 +18,19 @@
// Package capture_test runs a standalone server, loads seed data, executes
// generated queries, and writes responses as want/*.yaml files.
//
+// The capture loop is gated behind the CAPTURE_WANT_FIXTURES=1 environment
+// variable so a routine `make test` (or any `./...` discovery) cannot
+// silently re-baseline the golden fixtures from whatever branch the
+// developer happens to be on. Without the env var the test calls
+// t.Skip and returns instantly.
+//
// Usage:
//
-// go test -run TestCapture ./test/cases/measure/cmd/capture/ -args
[output-dir]
+// make capture-test-cases
+//
+// or directly:
+//
+// CAPTURE_WANT_FIXTURES=1 go test -run TestCapture
./test/cases/measure/cmd/capture/ -args [output-dir]
//
// output-dir defaults to ../../data (i.e. test/cases/measure/data).
package capture_test
@@ -49,6 +59,12 @@ import (
)
func TestCapture(t *testing.T) {
+ if os.Getenv("CAPTURE_WANT_FIXTURES") != "1" {
+ t.Skip("skipped: set CAPTURE_WANT_FIXTURES=1 (or run `make
capture-test-cases`) " +
+ "to re-baseline data/want/*.yaml. This test starts a
live server and rewrites " +
+ "the golden fixtures with whatever the current build
produces, so it must be " +
+ "opt-in and only run against a trusted baseline.")
+ }
gomega.RegisterTestingT(t)
// Initialize logger
@@ -97,6 +113,10 @@ func TestCapture(t *testing.T) {
entries, readErr := os.ReadDir(inputDirPath)
gomega.Expect(readErr).NotTo(gomega.HaveOccurred())
+ // Precompute the header bytes once so each fixture write is a single
+ // make+copy, not a per-iteration string→[]byte conversion + append.
+ headerBytes := []byte(casesMeasureData.LicenseHeader)
+
capturedCount := 0
for _, entry := range entries {
if entry.IsDir() {
@@ -169,7 +189,10 @@ func TestCapture(t *testing.T) {
}
wantPath := filepath.Join(wantDirPath, testName+".yaml")
- if writeErr := os.WriteFile(wantPath, respYAML, 0o600);
writeErr != nil {
+ wantContent := make([]byte, len(headerBytes)+len(respYAML))
+ copy(wantContent, headerBytes)
+ copy(wantContent[len(headerBytes):], respYAML)
+ if writeErr := os.WriteFile(wantPath, wantContent, 0o600);
writeErr != nil {
t.Errorf(" [ERROR] %s: write failed: %v", testName,
writeErr)
continue
}
diff --git a/test/cases/measure/cmd/generate/capture.go
b/test/cases/measure/cmd/generate/capture.go
index f4b79dc0a..1226c2e9b 100644
--- a/test/cases/measure/cmd/generate/capture.go
+++ b/test/cases/measure/cmd/generate/capture.go
@@ -76,6 +76,10 @@ func runCapture(outputDir, serverAddr string) {
skippedCount := 0
errorCount := 0
+ // Precompute the header bytes once so each fixture write is a single
+ // make+copy, not a per-iteration string→[]byte conversion + append.
+ headerBytes := []byte(licenseHeader)
+
for _, entry := range entries {
if entry.IsDir() {
continue
@@ -165,7 +169,10 @@ func runCapture(outputDir, serverAddr string) {
}
wantPath := filepath.Join(wantDirPath, testName+".yaml")
- if writeErr := os.WriteFile(wantPath, respYAML, 0o600);
writeErr != nil {
+ wantContent := make([]byte, len(headerBytes)+len(respYAML))
+ copy(wantContent, headerBytes)
+ copy(wantContent[len(headerBytes):], respYAML)
+ if writeErr := os.WriteFile(wantPath, wantContent, 0o600);
writeErr != nil {
fmt.Fprintf(os.Stderr, " [ERROR] %s: write failed:
%v\n", testName, writeErr)
errorCount++
continue
diff --git a/test/cases/measure/cmd/generate/types.go
b/test/cases/measure/cmd/generate/types.go
index 20f11fc1e..9eecaf6de 100644
--- a/test/cases/measure/cmd/generate/types.go
+++ b/test/cases/measure/cmd/generate/types.go
@@ -23,6 +23,7 @@ import (
measurev1
"github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
modelv1
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+ "github.com/apache/skywalking-banyandb/test/cases/measure/data"
)
// TestCase represents a generated test case with all its artifacts.
@@ -37,22 +38,7 @@ type TestCase struct {
DisOrder bool
}
-const licenseHeader = "# Licensed to Apache Software Foundation (ASF) under
one or more contributor\n" +
- "# license agreements. See the NOTICE file distributed with\n" +
- "# this work for additional information regarding copyright\n" +
- "# ownership. Apache Software Foundation (ASF) licenses this file to
you under\n" +
- "# the Apache License, Version 2.0 (the \"License\"); you may\n" +
- "# not use this file except in compliance with the License.\n" +
- "# You may obtain a copy of the License at\n" +
- "#\n" +
- "# http://www.apache.org/licenses/LICENSE-2.0\n" +
- "#\n" +
- "# Unless required by applicable law or agreed to in writing,\n" +
- "# software distributed under the License is distributed on an\n" +
- "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" +
- "# KIND, either express or implied. See the License for the\n" +
- "# specific language governing permissions and limitations\n" +
- "# under the License.\n\n"
+const licenseHeader = data.LicenseHeader
// QLFileContent returns the .ql file content with license header.
func (tc *TestCase) QLFileContent() []byte {
diff --git a/test/cases/measure/data/license_header.go
b/test/cases/measure/data/license_header.go
new file mode 100644
index 000000000..59b7e52a4
--- /dev/null
+++ b/test/cases/measure/data/license_header.go
@@ -0,0 +1,43 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package data
+
+// LicenseHeader is the Apache 2.0 header (in YAML/properties comment form,
+// trailing blank line included) that generated fixture files must carry so
+// they pass the repo-wide license-eye scan. Both the generator and the
+// response-capture writer prepend this to every file they produce; without
+// it, regenerating the fixtures locally silently strips the header and
+// would break `make license-check` (the bug uncovered by the
+// vectorized-query branch's full `make test` run, which exercises the
+// `./test/cases/...` path that CI normally skips).
+const LicenseHeader = "# Licensed to Apache Software Foundation (ASF) under
one or more contributor\n" +
+ "# license agreements. See the NOTICE file distributed with\n" +
+ "# this work for additional information regarding copyright\n" +
+ "# ownership. Apache Software Foundation (ASF) licenses this file to
you under\n" +
+ "# the Apache License, Version 2.0 (the \"License\"); you may\n" +
+ "# not use this file except in compliance with the License.\n" +
+ "# You may obtain a copy of the License at\n" +
+ "#\n" +
+ "# http://www.apache.org/licenses/LICENSE-2.0\n" +
+ "#\n" +
+ "# Unless required by applicable law or agreed to in writing,\n" +
+ "# software distributed under the License is distributed on an\n" +
+ "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" +
+ "# KIND, either express or implied. See the License for the\n" +
+ "# specific language governing permissions and limitations\n" +
+ "# under the License.\n\n"