You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

327 lines
9.7 KiB

  1. // Copyright 2015 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package bigquery
  15. import (
  16. "context"
  17. "errors"
  18. "strconv"
  19. "testing"
  20. "time"
  21. "cloud.google.com/go/internal/testutil"
  22. "github.com/google/go-cmp/cmp"
  23. bq "google.golang.org/api/bigquery/v2"
  24. itest "google.golang.org/api/iterator/testing"
  25. )
  26. // readServiceStub services read requests by returning data from an in-memory list of values.
  27. type listTablesStub struct {
  28. expectedProject, expectedDataset string
  29. tables []*bq.TableListTables
  30. }
  31. func (s *listTablesStub) listTables(it *TableIterator, pageSize int, pageToken string) (*bq.TableList, error) {
  32. if it.dataset.ProjectID != s.expectedProject {
  33. return nil, errors.New("wrong project id")
  34. }
  35. if it.dataset.DatasetID != s.expectedDataset {
  36. return nil, errors.New("wrong dataset id")
  37. }
  38. const maxPageSize = 2
  39. if pageSize <= 0 || pageSize > maxPageSize {
  40. pageSize = maxPageSize
  41. }
  42. start := 0
  43. if pageToken != "" {
  44. var err error
  45. start, err = strconv.Atoi(pageToken)
  46. if err != nil {
  47. return nil, err
  48. }
  49. }
  50. end := start + pageSize
  51. if end > len(s.tables) {
  52. end = len(s.tables)
  53. }
  54. nextPageToken := ""
  55. if end < len(s.tables) {
  56. nextPageToken = strconv.Itoa(end)
  57. }
  58. return &bq.TableList{
  59. Tables: s.tables[start:end],
  60. NextPageToken: nextPageToken,
  61. }, nil
  62. }
  63. func TestTables(t *testing.T) {
  64. c := &Client{projectID: "p1"}
  65. inTables := []*bq.TableListTables{
  66. {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t1"}},
  67. {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t2"}},
  68. {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t3"}},
  69. }
  70. outTables := []*Table{
  71. {ProjectID: "p1", DatasetID: "d1", TableID: "t1", c: c},
  72. {ProjectID: "p1", DatasetID: "d1", TableID: "t2", c: c},
  73. {ProjectID: "p1", DatasetID: "d1", TableID: "t3", c: c},
  74. }
  75. lts := &listTablesStub{
  76. expectedProject: "p1",
  77. expectedDataset: "d1",
  78. tables: inTables,
  79. }
  80. old := listTables
  81. listTables = lts.listTables // cannot use t.Parallel with this test
  82. defer func() { listTables = old }()
  83. msg, ok := itest.TestIterator(outTables,
  84. func() interface{} { return c.Dataset("d1").Tables(context.Background()) },
  85. func(it interface{}) (interface{}, error) { return it.(*TableIterator).Next() })
  86. if !ok {
  87. t.Error(msg)
  88. }
  89. }
  90. type listDatasetsStub struct {
  91. expectedProject string
  92. datasets []*bq.DatasetListDatasets
  93. hidden map[*bq.DatasetListDatasets]bool
  94. }
  95. func (s *listDatasetsStub) listDatasets(it *DatasetIterator, pageSize int, pageToken string) (*bq.DatasetList, error) {
  96. const maxPageSize = 2
  97. if pageSize <= 0 || pageSize > maxPageSize {
  98. pageSize = maxPageSize
  99. }
  100. if it.Filter != "" {
  101. return nil, errors.New("filter not supported")
  102. }
  103. if it.ProjectID != s.expectedProject {
  104. return nil, errors.New("bad project ID")
  105. }
  106. start := 0
  107. if pageToken != "" {
  108. var err error
  109. start, err = strconv.Atoi(pageToken)
  110. if err != nil {
  111. return nil, err
  112. }
  113. }
  114. var (
  115. i int
  116. result []*bq.DatasetListDatasets
  117. nextPageToken string
  118. )
  119. for i = start; len(result) < pageSize && i < len(s.datasets); i++ {
  120. if s.hidden[s.datasets[i]] && !it.ListHidden {
  121. continue
  122. }
  123. result = append(result, s.datasets[i])
  124. }
  125. if i < len(s.datasets) {
  126. nextPageToken = strconv.Itoa(i)
  127. }
  128. return &bq.DatasetList{
  129. Datasets: result,
  130. NextPageToken: nextPageToken,
  131. }, nil
  132. }
  133. func TestDatasets(t *testing.T) {
  134. client := &Client{projectID: "p"}
  135. inDatasets := []*bq.DatasetListDatasets{
  136. {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "a"}},
  137. {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "b"}},
  138. {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "hidden"}},
  139. {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "c"}},
  140. }
  141. outDatasets := []*Dataset{
  142. {"p", "a", client},
  143. {"p", "b", client},
  144. {"p", "hidden", client},
  145. {"p", "c", client},
  146. }
  147. lds := &listDatasetsStub{
  148. expectedProject: "p",
  149. datasets: inDatasets,
  150. hidden: map[*bq.DatasetListDatasets]bool{inDatasets[2]: true},
  151. }
  152. old := listDatasets
  153. listDatasets = lds.listDatasets // cannot use t.Parallel with this test
  154. defer func() { listDatasets = old }()
  155. msg, ok := itest.TestIterator(outDatasets,
  156. func() interface{} { it := client.Datasets(context.Background()); it.ListHidden = true; return it },
  157. func(it interface{}) (interface{}, error) { return it.(*DatasetIterator).Next() })
  158. if !ok {
  159. t.Fatalf("ListHidden=true: %s", msg)
  160. }
  161. msg, ok = itest.TestIterator([]*Dataset{outDatasets[0], outDatasets[1], outDatasets[3]},
  162. func() interface{} { it := client.Datasets(context.Background()); it.ListHidden = false; return it },
  163. func(it interface{}) (interface{}, error) { return it.(*DatasetIterator).Next() })
  164. if !ok {
  165. t.Fatalf("ListHidden=false: %s", msg)
  166. }
  167. }
  168. func TestDatasetToBQ(t *testing.T) {
  169. for _, test := range []struct {
  170. in *DatasetMetadata
  171. want *bq.Dataset
  172. }{
  173. {nil, &bq.Dataset{}},
  174. {&DatasetMetadata{Name: "name"}, &bq.Dataset{FriendlyName: "name"}},
  175. {&DatasetMetadata{
  176. Name: "name",
  177. Description: "desc",
  178. DefaultTableExpiration: time.Hour,
  179. Location: "EU",
  180. Labels: map[string]string{"x": "y"},
  181. Access: []*AccessEntry{{Role: OwnerRole, Entity: "example.com", EntityType: DomainEntity}},
  182. }, &bq.Dataset{
  183. FriendlyName: "name",
  184. Description: "desc",
  185. DefaultTableExpirationMs: 60 * 60 * 1000,
  186. Location: "EU",
  187. Labels: map[string]string{"x": "y"},
  188. Access: []*bq.DatasetAccess{{Role: "OWNER", Domain: "example.com"}},
  189. }},
  190. } {
  191. got, err := test.in.toBQ()
  192. if err != nil {
  193. t.Fatal(err)
  194. }
  195. if !testutil.Equal(got, test.want) {
  196. t.Errorf("%v:\ngot %+v\nwant %+v", test.in, got, test.want)
  197. }
  198. }
  199. // Check that non-writeable fields are unset.
  200. aTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local)
  201. for _, dm := range []*DatasetMetadata{
  202. {CreationTime: aTime},
  203. {LastModifiedTime: aTime},
  204. {FullID: "x"},
  205. {ETag: "e"},
  206. } {
  207. if _, err := dm.toBQ(); err == nil {
  208. t.Errorf("%+v: got nil, want error", dm)
  209. }
  210. }
  211. }
  212. func TestBQToDatasetMetadata(t *testing.T) {
  213. cTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local)
  214. cMillis := cTime.UnixNano() / 1e6
  215. mTime := time.Date(2017, 10, 31, 0, 0, 0, 0, time.Local)
  216. mMillis := mTime.UnixNano() / 1e6
  217. q := &bq.Dataset{
  218. CreationTime: cMillis,
  219. LastModifiedTime: mMillis,
  220. FriendlyName: "name",
  221. Description: "desc",
  222. DefaultTableExpirationMs: 60 * 60 * 1000,
  223. Location: "EU",
  224. Labels: map[string]string{"x": "y"},
  225. Access: []*bq.DatasetAccess{
  226. {Role: "READER", UserByEmail: "joe@example.com"},
  227. {Role: "WRITER", GroupByEmail: "users@example.com"},
  228. },
  229. Etag: "etag",
  230. }
  231. want := &DatasetMetadata{
  232. CreationTime: cTime,
  233. LastModifiedTime: mTime,
  234. Name: "name",
  235. Description: "desc",
  236. DefaultTableExpiration: time.Hour,
  237. Location: "EU",
  238. Labels: map[string]string{"x": "y"},
  239. Access: []*AccessEntry{
  240. {Role: ReaderRole, Entity: "joe@example.com", EntityType: UserEmailEntity},
  241. {Role: WriterRole, Entity: "users@example.com", EntityType: GroupEmailEntity},
  242. },
  243. ETag: "etag",
  244. }
  245. got, err := bqToDatasetMetadata(q)
  246. if err != nil {
  247. t.Fatal(err)
  248. }
  249. if diff := testutil.Diff(got, want); diff != "" {
  250. t.Errorf("-got, +want:\n%s", diff)
  251. }
  252. }
  253. func TestDatasetMetadataToUpdateToBQ(t *testing.T) {
  254. dm := DatasetMetadataToUpdate{
  255. Description: "desc",
  256. Name: "name",
  257. DefaultTableExpiration: time.Hour,
  258. }
  259. dm.SetLabel("label", "value")
  260. dm.DeleteLabel("del")
  261. got, err := dm.toBQ()
  262. if err != nil {
  263. t.Fatal(err)
  264. }
  265. want := &bq.Dataset{
  266. Description: "desc",
  267. FriendlyName: "name",
  268. DefaultTableExpirationMs: 60 * 60 * 1000,
  269. Labels: map[string]string{"label": "value"},
  270. ForceSendFields: []string{"Description", "FriendlyName"},
  271. NullFields: []string{"Labels.del"},
  272. }
  273. if diff := testutil.Diff(got, want); diff != "" {
  274. t.Errorf("-got, +want:\n%s", diff)
  275. }
  276. }
  277. func TestConvertAccessEntry(t *testing.T) {
  278. c := &Client{projectID: "pid"}
  279. for _, e := range []*AccessEntry{
  280. {Role: ReaderRole, Entity: "e", EntityType: DomainEntity},
  281. {Role: WriterRole, Entity: "e", EntityType: GroupEmailEntity},
  282. {Role: OwnerRole, Entity: "e", EntityType: UserEmailEntity},
  283. {Role: ReaderRole, Entity: "e", EntityType: SpecialGroupEntity},
  284. {Role: ReaderRole, EntityType: ViewEntity,
  285. View: &Table{ProjectID: "p", DatasetID: "d", TableID: "t", c: c}},
  286. } {
  287. q, err := e.toBQ()
  288. if err != nil {
  289. t.Fatal(err)
  290. }
  291. got, err := bqToAccessEntry(q, c)
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. if diff := testutil.Diff(got, e, cmp.AllowUnexported(Table{}, Client{})); diff != "" {
  296. t.Errorf("got=-, want=+:\n%s", diff)
  297. }
  298. }
  299. e := &AccessEntry{Role: ReaderRole, Entity: "e"}
  300. if _, err := e.toBQ(); err == nil {
  301. t.Error("got nil, want error")
  302. }
  303. if _, err := bqToAccessEntry(&bq.DatasetAccess{Role: "WRITER"}, nil); err == nil {
  304. t.Error("got nil, want error")
  305. }
  306. }