Files
opentf/internal/command/e2etest/json_dual_test.go
2026-04-02 07:11:07 -04:00

95 lines
2.9 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package e2etest
import (
"os"
"path/filepath"
"regexp"
"testing"
"github.com/opentofu/opentofu/internal/e2e"
)
func TestJsonIntoStream(t *testing.T) {
fixturePath := filepath.Join("testdata", "json_dual")
tf := e2e.NewBinary(t, tofuBin, fixturePath)
tfInto := e2e.NewBinary(t, tofuBin, fixturePath)
logTimestampRe := regexp.MustCompile(`,"@timestamp":"[^"]*"`)
resourceIdRe := regexp.MustCompile(`[a-z0-9\-]{36}`)
// state_lock_acquire/release messages are emitted only when lock acquisition
// exceeds a timer threshold. On Windows the two runs have different latencies,
// causing one to emit the message and the other not to. Strip these lines so
// the comparison is not timing-sensitive. See: https://github.com/opentofu/opentofu/issues/3918
stateLockRe := regexp.MustCompile(`(?m)^[^\n]*"type":"state_lock_(?:acquire|release)"[^\n]*\n?`)
// Elapsed durations can be flaky due to timing differences between the two runs, so let's remove those
elapsedSecondsRe := regexp.MustCompile(`"elapsed_seconds":\d+`)
afterDurationRe := regexp.MustCompile(`after \d+s`)
sanitize := func(s string) string {
s = logTimestampRe.ReplaceAllString(s, "")
s = resourceIdRe.ReplaceAllString(s, "<ident>")
s = stateLockRe.ReplaceAllString(s, "")
s = elapsedSecondsRe.ReplaceAllString(s, `"elapsed_seconds":0`)
s = afterDurationRe.ReplaceAllString(s, "after 0s")
return s
}
cases := []struct {
title string
args []string
}{
{"init", []string{"init"}},
{"validate", []string{"validate"}},
{"plan", []string{"plan"}},
{"apply", []string{"apply", "-auto-approve"}},
{"output", []string{"output"}},
}
for _, tc := range cases {
t.Run(tc.title, func(t *testing.T) {
// Run init -json
stdoutJson, stderr, err := tf.Run(append(tc.args, "-json")...)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if stderr != "" {
t.Errorf("unexpected stderr output:\n%s", stderr)
}
if stdoutJson[0] != byte('{') {
t.Errorf("Expected json output on stdout")
}
// Run init -json-into
jsonIntoPath := tfInto.Path("into.json")
stdoutJsonInto, stderr, err := tfInto.Run(append(tc.args, "-json-into="+jsonIntoPath)...)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if stderr != "" {
t.Errorf("unexpected stderr output:\n%s", stderr)
}
if stdoutJsonInto[0] == byte('{') {
t.Errorf("Unexpected json output on stdout")
}
// Read results
fileJson, err := os.ReadFile(jsonIntoPath)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
// Compare
fileJsonString := sanitize(string(fileJson))
stdoutJson = sanitize(stdoutJson)
if fileJsonString != stdoutJson {
t.Errorf("\nGot:\n%s\n\nExpected:\n%s", fileJsonString, stdoutJson)
}
})
}
}