-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathkernel_experimental_test.go
More file actions
160 lines (148 loc) · 6.27 KB
/
Copy pathkernel_experimental_test.go
File metadata and controls
160 lines (148 loc) · 6.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package dbsql
import (
"context"
"errors"
"reflect"
"testing"
"github.com/databricks/databricks-sql-go/internal/config"
dbsqlerr "github.com/databricks/databricks-sql-go/errors"
)
// This file is untagged (no cgo) so its tests — the experimental-option
// classification guard, the option→config wiring, and the Thrift fail-loud
// signal — run in the default CGO_ENABLED=0 build alongside the other
// kernel_config tests.
// kernelExperimentalFieldDisposition records how each experimental (kernel-only)
// option is handled. Every experimental field is forwarded to the kernel C ABI
// (there is no "inert" experimental knob — they exist precisely because the kernel
// supports them) and rejected on the Thrift path (the connector fails loud when
// KernelExperimental is non-nil). A new field on config.KernelExperimentalConfig
// without an entry here fails TestKernelExperimentalFieldsClassified, forcing a
// deliberate decision and a setter in KernelBackend.OpenSession so it can't be
// silently dropped.
var kernelExperimentalFieldDisposition = map[string]string{
"TLSTrustedCertsPEM": "forwarded", // set_tls_trusted_certs
"TLSSkipHostnameVerify": "forwarded", // set_tls_skip_hostname_verification
}
func TestKernelExperimentalFieldsClassified(t *testing.T) {
tp := reflect.TypeOf(config.KernelExperimentalConfig{})
classified := make(map[string]bool, tp.NumField())
for i := 0; i < tp.NumField(); i++ {
name := tp.Field(i).Name
classified[name] = true
if _, ok := kernelExperimentalFieldDisposition[name]; !ok {
t.Errorf("config.KernelExperimentalConfig field %q is not classified. Add it to "+
"kernelExperimentalFieldDisposition and wire a setter in KernelBackend.OpenSession / "+
"newKernelBackend so it isn't silently dropped on the kernel path.", name)
}
}
for name := range kernelExperimentalFieldDisposition {
if !classified[name] {
t.Errorf("kernelExperimentalFieldDisposition has %q but config.KernelExperimentalConfig no longer does; remove it", name)
}
}
}
// The experimental WithKernel* options are rejected on the default (Thrift) path
// so a caller who forgets WithUseKernel learns the option had no effect. The
// option builders set config.KernelExperimental; the connector's backend-selection
// branch is what rejects it. We assert the option→config wiring here (a non-nil
// KernelExperimental after applying a WithKernel* option is the signal the Thrift
// branch keys off).
func TestWithKernelTLSOptionsSetExperimental(t *testing.T) {
cases := []struct {
name string
opt ConnOption
verify func(*config.KernelExperimentalConfig) bool
}{
{"trusted certs", WithKernelTrustedCerts([]byte("ca")), func(k *config.KernelExperimentalConfig) bool {
return string(k.TLSTrustedCertsPEM) == "ca"
}},
{"skip hostname", WithKernelSkipHostnameVerify(), func(k *config.KernelExperimentalConfig) bool {
return k.TLSSkipHostnameVerify
}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
cfg := config.WithDefaults()
c.opt(cfg)
if cfg.KernelExperimental == nil {
t.Fatalf("%s: KernelExperimental should be non-nil after the option", c.name)
}
if !c.verify(cfg.KernelExperimental) {
t.Errorf("%s: option did not set the expected field(s): %+v", c.name, cfg.KernelExperimental)
}
})
}
}
// End-to-end: setting a WithKernel* option WITHOUT WithUseKernel must make
// Connect fail loud on the default (Thrift) path rather than silently connect
// with a weaker-than-intended trust store. This exercises the connector's
// reject branch (not just option→config wiring) and asserts the error wraps the
// ErrRequiresKernelBackend sentinel so callers can detect it with errors.Is —
// the mirror of TestKernelBackendNotCompiledIn. Runs in the default
// CGO_ENABLED=0 build (no kernel linked in).
func TestWithKernelOptionsRejectedOnThriftPath(t *testing.T) {
cases := []struct {
name string
opt ConnOption
}{
{"trusted certs", WithKernelTrustedCerts([]byte("ca"))},
{"skip hostname", WithKernelSkipHostnameVerify()},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c, err := NewConnector(
WithServerHostname("example.cloud.databricks.com"),
WithPort(443),
WithHTTPPath("/sql/1.0/endpoints/12346a5b5b0e123a"),
WithAccessToken("supersecret"),
tc.opt,
)
if err != nil {
t.Fatalf("NewConnector: %v", err)
}
// No WithUseKernel — the Thrift path must reject the kernel-only option.
if _, err = c.Connect(context.Background()); err == nil {
t.Fatal("Connect should reject a WithKernel* option on the Thrift path, got nil")
} else if !errors.Is(err, dbsqlerr.ErrRequiresKernelBackend) {
t.Errorf("error should wrap ErrRequiresKernelBackend; got: %v", err)
}
})
}
}
// WithKernelTrustedCerts must copy the PEM defensively, so a caller mutating its
// slice after applying the option (but before Connect) can't change the stored
// trust store. This is the option-set counterpart to TestKernelExperimentalDeepCopy
// (which covers the per-conn DeepCopy path).
func TestWithKernelTrustedCertsCopiesPEM(t *testing.T) {
pem := []byte("ca-bundle")
cfg := config.WithDefaults()
WithKernelTrustedCerts(pem)(cfg)
// Mutate the caller's slice after the option ran.
pem[0] = 'X'
if cfg.KernelExperimental == nil {
t.Fatal("KernelExperimental should be non-nil after WithKernelTrustedCerts")
}
if got := string(cfg.KernelExperimental.TLSTrustedCertsPEM); got != "ca-bundle" {
t.Errorf("WithKernelTrustedCerts aliased the caller's slice; stored %q, want %q", got, "ca-bundle")
}
}
// DeepCopy must copy the CA byte slice, not alias it — the connector may DeepCopy
// the whole Config per conn, and a shared backing array would let one conn's
// mutation reach another.
func TestKernelExperimentalDeepCopy(t *testing.T) {
orig := &config.KernelExperimentalConfig{
TLSTrustedCertsPEM: []byte("ca-bundle"),
TLSSkipHostnameVerify: true,
}
cp := orig.DeepCopy()
if cp == nil || string(cp.TLSTrustedCertsPEM) != "ca-bundle" || !cp.TLSSkipHostnameVerify {
t.Fatalf("DeepCopy lost data: %+v", cp)
}
cp.TLSTrustedCertsPEM[0] = 'X'
if orig.TLSTrustedCertsPEM[0] == 'X' {
t.Error("DeepCopy aliased the CA byte slice; a copy mutation reached the original")
}
if (*config.KernelExperimentalConfig)(nil).DeepCopy() != nil {
t.Error("nil.DeepCopy() should be nil")
}
}