1
2
3
4
5
6
7
8
9
10
11
12
13
14 package bigquery
15
16 import (
17 "fmt"
18 "testing"
19 "time"
20
21 "cloud.google.com/go/internal/testutil"
22 bq "google.golang.org/api/bigquery/v2"
23 )
24
25 func testRoutineConversion(t *testing.T, conversion string, in interface{}, want interface{}) {
26 var got interface{}
27 var err error
28 switch conversion {
29 case "ToRoutineMetadata":
30 input, ok := in.(*bq.Routine)
31 if !ok {
32 t.Fatalf("failed input type conversion (bq.Routine): %v", in)
33 }
34 got, err = bqToRoutineMetadata(input)
35 case "FromRoutineMetadata":
36 input, ok := in.(*RoutineMetadata)
37 if !ok {
38 t.Fatalf("failed input type conversion (bq.RoutineMetadata): %v", in)
39 }
40 got, err = input.toBQ()
41 case "FromRoutineMetadataToUpdate":
42 input, ok := in.(*RoutineMetadataToUpdate)
43 if !ok {
44 t.Fatalf("failed input type conversion: %v", in)
45 }
46 got, err = input.toBQ()
47 case "ToRoutineArgument":
48 input, ok := in.(*bq.Argument)
49 if !ok {
50 t.Fatalf("failed input type conversion: %v", in)
51 }
52 got, err = bqToRoutineArgument(input)
53 case "FromRoutineArgument":
54 input, ok := in.(*RoutineArgument)
55 if !ok {
56 t.Fatalf("failed input type conversion: %v", in)
57 }
58 got, err = input.toBQ()
59 default:
60 t.Fatalf("invalid comparison: %s", conversion)
61 }
62 if err != nil {
63 t.Fatalf("failed conversion function for %q: %v", conversion, err)
64 }
65 if diff := testutil.Diff(got, want); diff != "" {
66 t.Fatalf("%+v: -got, +want:\n%s", in, diff)
67 }
68 }
69
70 func TestRoutineTypeConversions(t *testing.T) {
71 aTime := time.Date(2019, 3, 14, 0, 0, 0, 0, time.Local)
72 aTimeMillis := aTime.UnixNano() / 1e6
73
74 tests := []struct {
75 name string
76 conversion string
77 in interface{}
78 want interface{}
79 }{
80 {
81 name: "empty",
82 conversion: "ToRoutineMetadata",
83 in: &bq.Routine{},
84 want: &RoutineMetadata{},
85 },
86 {
87 name: "empty",
88 conversion: "FromRoutineMetadata",
89 in: &RoutineMetadata{},
90 want: &bq.Routine{},
91 },
92 {
93 name: "basic",
94 conversion: "ToRoutineMetadata",
95 in: &bq.Routine{
96 CreationTime: aTimeMillis,
97 LastModifiedTime: aTimeMillis,
98 DefinitionBody: "body",
99 Description: "desc",
100 Etag: "etag",
101 DeterminismLevel: "DETERMINISTIC",
102 RoutineType: "type",
103 Language: "lang",
104 ReturnType: &bq.StandardSqlDataType{TypeKind: "INT64"},
105 ReturnTableType: &bq.StandardSqlTableType{
106 Columns: []*bq.StandardSqlField{
107 {Name: "field", Type: &bq.StandardSqlDataType{TypeKind: "FLOAT64"}},
108 },
109 },
110 DataGovernanceType: "DATA_MASKING",
111 },
112 want: &RoutineMetadata{
113 CreationTime: aTime,
114 LastModifiedTime: aTime,
115 Description: "desc",
116 DeterminismLevel: Deterministic,
117 Body: "body",
118 ETag: "etag",
119 Type: "type",
120 Language: "lang",
121 ReturnType: &StandardSQLDataType{TypeKind: "INT64"},
122 ReturnTableType: &StandardSQLTableType{
123 Columns: []*StandardSQLField{
124 {Name: "field", Type: &StandardSQLDataType{TypeKind: "FLOAT64"}},
125 },
126 },
127 DataGovernanceType: "DATA_MASKING",
128 },
129 },
130 {
131 name: "basic",
132 conversion: "FromRoutineMetadata",
133 in: &RoutineMetadata{
134 Description: "desc",
135 DeterminismLevel: Deterministic,
136 Body: "body",
137 Type: "type",
138 Language: "lang",
139 ReturnType: &StandardSQLDataType{TypeKind: "INT64"},
140 ReturnTableType: &StandardSQLTableType{
141 Columns: []*StandardSQLField{
142 {Name: "field", Type: &StandardSQLDataType{TypeKind: "FLOAT64"}},
143 },
144 },
145 DataGovernanceType: "DATA_MASKING",
146 },
147 want: &bq.Routine{
148 DefinitionBody: "body",
149 Description: "desc",
150 DeterminismLevel: "DETERMINISTIC",
151 RoutineType: "type",
152 Language: "lang",
153 ReturnType: &bq.StandardSqlDataType{TypeKind: "INT64"},
154 ReturnTableType: &bq.StandardSqlTableType{
155 Columns: []*bq.StandardSqlField{
156 {Name: "field", Type: &bq.StandardSqlDataType{TypeKind: "FLOAT64"}},
157 },
158 },
159 DataGovernanceType: "DATA_MASKING",
160 },
161 },
162 {
163 name: "body_and_libs",
164 conversion: "FromRoutineMetadataToUpdate",
165 in: &RoutineMetadataToUpdate{
166 Body: "body",
167 ImportedLibraries: []string{"foo", "bar"},
168 ReturnType: &StandardSQLDataType{TypeKind: "FOO"},
169 DataGovernanceType: "DATA_MASKING",
170 },
171 want: &bq.Routine{
172 DefinitionBody: "body",
173 ImportedLibraries: []string{"foo", "bar"},
174 ReturnType: &bq.StandardSqlDataType{TypeKind: "FOO"},
175 DataGovernanceType: "DATA_MASKING",
176 ForceSendFields: []string{"DefinitionBody", "ImportedLibraries", "ReturnType", "DataGovernanceType"},
177 },
178 },
179 {
180 name: "null_fields",
181 conversion: "FromRoutineMetadataToUpdate",
182 in: &RoutineMetadataToUpdate{
183 Type: "type",
184 Arguments: []*RoutineArgument{},
185 ImportedLibraries: []string{},
186 },
187 want: &bq.Routine{
188 RoutineType: "type",
189 ForceSendFields: []string{"RoutineType"},
190 NullFields: []string{"Arguments", "ImportedLibraries"},
191 },
192 },
193 {
194 name: "empty",
195 conversion: "ToRoutineArgument",
196 in: &bq.Argument{},
197 want: &RoutineArgument{}},
198 {
199 name: "basic",
200 conversion: "ToRoutineArgument",
201 in: &bq.Argument{
202 Name: "foo",
203 ArgumentKind: "bar",
204 Mode: "baz",
205 },
206 want: &RoutineArgument{
207 Name: "foo",
208 Kind: "bar",
209 Mode: "baz",
210 },
211 },
212 {
213 name: "empty",
214 conversion: "FromRoutineArgument",
215 in: &RoutineArgument{},
216 want: &bq.Argument{},
217 },
218 {
219 name: "basic",
220 conversion: "FromRoutineArgument",
221 in: &RoutineArgument{
222 Name: "foo",
223 Kind: "bar",
224 Mode: "baz",
225 },
226 want: &bq.Argument{
227 Name: "foo",
228 ArgumentKind: "bar",
229 Mode: "baz",
230 }},
231 }
232
233 for _, test := range tests {
234 t.Run(fmt.Sprintf("%s/%s", test.conversion, test.name), func(t *testing.T) {
235 testRoutineConversion(t, test.conversion, test.in, test.want)
236 })
237 }
238 }
239
240 func TestRoutineIdentifiers(t *testing.T) {
241 testRoutine := &Routine{
242 ProjectID: "p",
243 DatasetID: "d",
244 RoutineID: "r",
245 c: nil,
246 }
247 for _, tc := range []struct {
248 description string
249 in *Routine
250 format IdentifierFormat
251 want string
252 wantErr bool
253 }{
254 {
255 description: "empty format string",
256 in: testRoutine,
257 format: "",
258 wantErr: true,
259 },
260 {
261 description: "legacy",
262 in: testRoutine,
263 wantErr: true,
264 },
265 {
266 description: "standard unquoted",
267 in: testRoutine,
268 format: StandardSQLID,
269 want: "p.d.r",
270 },
271 {
272 description: "standard w/dash",
273 in: &Routine{ProjectID: "p-p", DatasetID: "d", RoutineID: "r"},
274 format: StandardSQLID,
275 want: "`p-p`.d.r",
276 },
277 {
278 description: "api resource",
279 in: testRoutine,
280 format: StorageAPIResourceID,
281 wantErr: true,
282 },
283 } {
284 got, err := tc.in.Identifier(tc.format)
285 if tc.wantErr && err == nil {
286 t.Errorf("case %q: wanted err, was success", tc.description)
287 }
288 if !tc.wantErr {
289 if err != nil {
290 t.Errorf("case %q: wanted success, got err: %v", tc.description, err)
291 } else {
292 if got != tc.want {
293 t.Errorf("case %q: got %s, want %s", tc.description, got, tc.want)
294 }
295 }
296 }
297 }
298 }
299
View as plain text