athenaintegration_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. package aws
  2. import (
  3. "os"
  4. "reflect"
  5. "testing"
  6. "time"
  7. "github.com/aws/aws-sdk-go-v2/service/athena/types"
  8. "github.com/opencost/opencost/core/pkg/opencost"
  9. "github.com/opencost/opencost/core/pkg/util/json"
  10. "github.com/opencost/opencost/core/pkg/util/timeutil"
  11. )
  12. func TestAthenaIntegration_GetCloudCost(t *testing.T) {
  13. athenaConfigPath := os.Getenv("ATHENA_CONFIGURATION")
  14. if athenaConfigPath == "" {
  15. t.Skip("skipping integration test, set environment variable ATHENA_CONFIGURATION")
  16. }
  17. athenaConfigBin, err := os.ReadFile(athenaConfigPath)
  18. if err != nil {
  19. t.Fatalf("failed to read config file: %s", err.Error())
  20. }
  21. var athenaConfig AthenaConfiguration
  22. err = json.Unmarshal(athenaConfigBin, &athenaConfig)
  23. if err != nil {
  24. t.Fatalf("failed to unmarshal config from JSON: %s", err.Error())
  25. }
  26. testCases := map[string]struct {
  27. integration *AthenaIntegration
  28. start time.Time
  29. end time.Time
  30. expected bool
  31. }{
  32. // No CUR data is expected within 2 days of now
  33. "too_recent_window": {
  34. integration: &AthenaIntegration{
  35. AthenaQuerier: AthenaQuerier{
  36. AthenaConfiguration: athenaConfig,
  37. },
  38. },
  39. end: time.Now(),
  40. start: time.Now().Add(-timeutil.Day),
  41. expected: true,
  42. },
  43. // CUR data should be available
  44. "last week window": {
  45. integration: &AthenaIntegration{
  46. AthenaQuerier: AthenaQuerier{
  47. AthenaConfiguration: athenaConfig,
  48. },
  49. },
  50. end: time.Now().Add(-7 * timeutil.Day),
  51. start: time.Now().Add(-8 * timeutil.Day),
  52. expected: false,
  53. },
  54. }
  55. for name, testCase := range testCases {
  56. t.Run(name, func(t *testing.T) {
  57. actual, err := testCase.integration.GetCloudCost(testCase.start, testCase.end)
  58. if err != nil {
  59. t.Errorf("Other error during testing %s", err)
  60. } else if actual.IsEmpty() != testCase.expected {
  61. t.Errorf("Incorrect result, actual emptiness: %t, expected: %t", actual.IsEmpty(), testCase.expected)
  62. }
  63. })
  64. }
  65. }
  66. func Test_athenaRowToCloudCost(t *testing.T) {
  67. aqiCur10 := AthenaQueryIndexes{
  68. ColumnIndexes: map[string]int{
  69. "ListCostColumn": 0,
  70. "NetCostColumn": 1,
  71. "AmortizedNetCostColumn": 2,
  72. "AmortizedCostColumn": 3,
  73. "IsK8sColumn": 4,
  74. AthenaDateTruncColumn: 5,
  75. "line_item_resource_id": 6,
  76. "bill_payer_account_id": 7,
  77. "line_item_usage_account_id": 8,
  78. "line_item_product_code": 9,
  79. "line_item_usage_type": 10,
  80. "product_region_code": 11,
  81. "line_item_availability_zone": 12,
  82. "resource_tags_user_test": 13,
  83. "resource_tags_aws_test": 14,
  84. },
  85. TagColumns: []string{"resource_tags_user_test"},
  86. AWSTagColumns: []string{"resource_tags_aws_test"},
  87. ListCostColumn: "ListCostColumn",
  88. NetCostColumn: "NetCostColumn",
  89. AmortizedNetCostColumn: "AmortizedNetCostColumn",
  90. AmortizedCostColumn: "AmortizedCostColumn",
  91. IsK8sColumn: "IsK8sColumn",
  92. }
  93. aqiCur20 := AthenaQueryIndexes{
  94. ColumnIndexes: map[string]int{
  95. "ListCostColumn": 0,
  96. "NetCostColumn": 1,
  97. "AmortizedNetCostColumn": 2,
  98. "AmortizedCostColumn": 3,
  99. "IsK8sColumn": 4,
  100. AthenaDateTruncColumn: 5,
  101. "line_item_resource_id": 6,
  102. "bill_payer_account_id": 7,
  103. "line_item_usage_account_id": 8,
  104. "line_item_product_code": 9,
  105. "line_item_usage_type": 10,
  106. "product_region_code": 11,
  107. "line_item_availability_zone": 12,
  108. AthenaResourceTagsCastToJsonColumn: 13,
  109. },
  110. TagColumns: []string{},
  111. AWSTagColumns: []string{},
  112. ListCostColumn: "ListCostColumn",
  113. NetCostColumn: "NetCostColumn",
  114. AmortizedNetCostColumn: "AmortizedNetCostColumn",
  115. AmortizedCostColumn: "AmortizedCostColumn",
  116. IsK8sColumn: "IsK8sColumn",
  117. }
  118. tests := []struct {
  119. name string
  120. row []string
  121. aqi AthenaQueryIndexes
  122. want *opencost.CloudCost
  123. wantErr bool
  124. }{
  125. {
  126. name: "incorrect row length CUR 1.0",
  127. row: []string{"not enough elements"},
  128. aqi: aqiCur10,
  129. want: nil,
  130. wantErr: true,
  131. },
  132. {
  133. name: "invalid list cost CUR 1.0",
  134. row: []string{"invalid", "2", "3", "4", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  135. aqi: aqiCur10,
  136. want: nil,
  137. wantErr: true,
  138. },
  139. {
  140. name: "invalid net cost CUR 1.0",
  141. row: []string{"1", "invalid", "3", "4", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  142. aqi: aqiCur10,
  143. want: nil,
  144. wantErr: true,
  145. },
  146. {
  147. name: "invalid amortized net cost CUR 1.0",
  148. row: []string{"1", "2", "invalid", "4", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  149. aqi: aqiCur10,
  150. want: nil,
  151. wantErr: true,
  152. },
  153. {
  154. name: "invalid amortized cost CUR 1.0",
  155. row: []string{"1", "2", "3", "invalid", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  156. aqi: aqiCur10,
  157. want: nil,
  158. wantErr: true,
  159. },
  160. {
  161. name: "invalid date CUR 1.0",
  162. row: []string{"1", "2", "3", "4", "true", "invalid", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  163. aqi: aqiCur10,
  164. want: nil,
  165. wantErr: true,
  166. },
  167. {
  168. name: "valid kubernetes with labels CUR 1.0",
  169. row: []string{"1", "2", "3", "4", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "userTagTestValue", "awsTagTestValue"},
  170. aqi: aqiCur10,
  171. want: &opencost.CloudCost{
  172. Properties: &opencost.CloudCostProperties{
  173. ProviderID: "resourceID",
  174. Provider: "AWS",
  175. AccountID: "usageAccountID",
  176. AccountName: "usageAccountID",
  177. InvoiceEntityID: "payerAccountID",
  178. InvoiceEntityName: "payerAccountID",
  179. RegionID: "regionCode",
  180. AvailabilityZone: "availabilityZone",
  181. Service: "productCode",
  182. Category: opencost.OtherCategory,
  183. Labels: opencost.CloudCostLabels{
  184. "test": "userTagTestValue",
  185. "aws_test": "awsTagTestValue",
  186. },
  187. },
  188. Window: opencost.NewClosedWindow(
  189. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  190. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  191. ),
  192. ListCost: opencost.CostMetric{
  193. Cost: 1,
  194. KubernetesPercent: 1,
  195. },
  196. NetCost: opencost.CostMetric{
  197. Cost: 2,
  198. KubernetesPercent: 1,
  199. },
  200. AmortizedNetCost: opencost.CostMetric{
  201. Cost: 3,
  202. KubernetesPercent: 1,
  203. },
  204. InvoicedCost: opencost.CostMetric{
  205. Cost: 2,
  206. KubernetesPercent: 1,
  207. },
  208. AmortizedCost: opencost.CostMetric{
  209. Cost: 4,
  210. KubernetesPercent: 1,
  211. },
  212. },
  213. wantErr: false,
  214. },
  215. {
  216. name: "valid non-kubernetes, no labels",
  217. row: []string{"1", "2", "3", "4", "false", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", "", ""},
  218. aqi: aqiCur10,
  219. want: &opencost.CloudCost{
  220. Properties: &opencost.CloudCostProperties{
  221. ProviderID: "resourceID",
  222. Provider: "AWS",
  223. AccountID: "usageAccountID",
  224. AccountName: "usageAccountID",
  225. InvoiceEntityID: "payerAccountID",
  226. InvoiceEntityName: "payerAccountID",
  227. RegionID: "regionCode",
  228. AvailabilityZone: "availabilityZone",
  229. Service: "productCode",
  230. Category: opencost.OtherCategory,
  231. Labels: opencost.CloudCostLabels{},
  232. },
  233. Window: opencost.NewClosedWindow(
  234. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  235. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  236. ),
  237. ListCost: opencost.CostMetric{
  238. Cost: 1,
  239. KubernetesPercent: 0,
  240. },
  241. NetCost: opencost.CostMetric{
  242. Cost: 2,
  243. KubernetesPercent: 0,
  244. },
  245. AmortizedNetCost: opencost.CostMetric{
  246. Cost: 3,
  247. KubernetesPercent: 0,
  248. },
  249. InvoicedCost: opencost.CostMetric{
  250. Cost: 2,
  251. KubernetesPercent: 0,
  252. },
  253. AmortizedCost: opencost.CostMetric{
  254. Cost: 4,
  255. KubernetesPercent: 0,
  256. },
  257. },
  258. wantErr: false,
  259. },
  260. {
  261. name: "valid load balancer product code CUR 1.0",
  262. row: []string{"1", "2", "3", "4", "false", "2024-09-01 00:00:00.000", "resourceID/lbID", "payerAccountID", "usageAccountID", "AWSELB", "usageType", "regionCode", "availabilityZone", "", ""},
  263. aqi: aqiCur10,
  264. want: &opencost.CloudCost{
  265. Properties: &opencost.CloudCostProperties{
  266. ProviderID: "lbID",
  267. Provider: "AWS",
  268. AccountID: "usageAccountID",
  269. AccountName: "usageAccountID",
  270. InvoiceEntityID: "payerAccountID",
  271. InvoiceEntityName: "payerAccountID",
  272. RegionID: "regionCode",
  273. AvailabilityZone: "availabilityZone",
  274. Service: "AWSELB",
  275. Category: opencost.NetworkCategory,
  276. Labels: opencost.CloudCostLabels{},
  277. },
  278. Window: opencost.NewClosedWindow(
  279. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  280. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  281. ),
  282. ListCost: opencost.CostMetric{
  283. Cost: 1,
  284. KubernetesPercent: 0,
  285. },
  286. NetCost: opencost.CostMetric{
  287. Cost: 2,
  288. KubernetesPercent: 0,
  289. },
  290. AmortizedNetCost: opencost.CostMetric{
  291. Cost: 3,
  292. KubernetesPercent: 0,
  293. },
  294. InvoicedCost: opencost.CostMetric{
  295. Cost: 2,
  296. KubernetesPercent: 0,
  297. },
  298. AmortizedCost: opencost.CostMetric{
  299. Cost: 4,
  300. KubernetesPercent: 0,
  301. },
  302. },
  303. wantErr: false,
  304. },
  305. {
  306. name: "valid non-kubernetes, Fargate CPU CUR 1.0",
  307. row: []string{"1", "2", "3", "4", "false", "2024-09-01 00:00:00.000", "123:pod/resource", "payerAccountID", "usageAccountID", "AmazonEKS", "CPU", "regionCode", "availabilityZone", "", ""},
  308. aqi: aqiCur10,
  309. want: &opencost.CloudCost{
  310. Properties: &opencost.CloudCostProperties{
  311. ProviderID: "123:pod/resource/CPU",
  312. Provider: "AWS",
  313. AccountID: "usageAccountID",
  314. AccountName: "usageAccountID",
  315. InvoiceEntityID: "payerAccountID",
  316. InvoiceEntityName: "payerAccountID",
  317. RegionID: "regionCode",
  318. AvailabilityZone: "availabilityZone",
  319. Service: "AmazonEKS",
  320. Category: opencost.ComputeCategory,
  321. Labels: opencost.CloudCostLabels{},
  322. },
  323. Window: opencost.NewClosedWindow(
  324. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  325. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  326. ),
  327. ListCost: opencost.CostMetric{
  328. Cost: 1,
  329. KubernetesPercent: 0,
  330. },
  331. NetCost: opencost.CostMetric{
  332. Cost: 2,
  333. KubernetesPercent: 0,
  334. },
  335. AmortizedNetCost: opencost.CostMetric{
  336. Cost: 3,
  337. KubernetesPercent: 0,
  338. },
  339. InvoicedCost: opencost.CostMetric{
  340. Cost: 2,
  341. KubernetesPercent: 0,
  342. },
  343. AmortizedCost: opencost.CostMetric{
  344. Cost: 4,
  345. KubernetesPercent: 0,
  346. },
  347. },
  348. wantErr: false,
  349. },
  350. {
  351. name: "valid non-kubernetes, Fargate RAM CUR 1.0",
  352. row: []string{"1", "2", "3", "4", "false", "2024-09-01 00:00:00.000", "123:pod/resource", "payerAccountID", "usageAccountID", "AmazonEKS", "GB", "regionCode", "availabilityZone", "", ""},
  353. aqi: aqiCur10,
  354. want: &opencost.CloudCost{
  355. Properties: &opencost.CloudCostProperties{
  356. ProviderID: "123:pod/resource/RAM",
  357. Provider: "AWS",
  358. AccountID: "usageAccountID",
  359. AccountName: "usageAccountID",
  360. InvoiceEntityID: "payerAccountID",
  361. InvoiceEntityName: "payerAccountID",
  362. RegionID: "regionCode",
  363. AvailabilityZone: "availabilityZone",
  364. Service: "AmazonEKS",
  365. Category: opencost.ComputeCategory,
  366. Labels: opencost.CloudCostLabels{},
  367. },
  368. Window: opencost.NewClosedWindow(
  369. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  370. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  371. ),
  372. ListCost: opencost.CostMetric{
  373. Cost: 1,
  374. KubernetesPercent: 0,
  375. },
  376. NetCost: opencost.CostMetric{
  377. Cost: 2,
  378. KubernetesPercent: 0,
  379. },
  380. AmortizedNetCost: opencost.CostMetric{
  381. Cost: 3,
  382. KubernetesPercent: 0,
  383. },
  384. InvoicedCost: opencost.CostMetric{
  385. Cost: 2,
  386. KubernetesPercent: 0,
  387. },
  388. AmortizedCost: opencost.CostMetric{
  389. Cost: 4,
  390. KubernetesPercent: 0,
  391. },
  392. },
  393. wantErr: false,
  394. },
  395. {
  396. name: "valid kubernetes with labels CUR 2.0",
  397. row: []string{"1", "2", "3", "4", "true", "2024-09-01 00:00:00.000", "resourceID", "payerAccountID", "usageAccountID", "productCode", "usageType", "regionCode", "availabilityZone", `{"test": "userTagTestValue", "aws_test": "awsTagTestValue"}`},
  398. aqi: aqiCur20,
  399. want: &opencost.CloudCost{
  400. Properties: &opencost.CloudCostProperties{
  401. ProviderID: "resourceID",
  402. Provider: "AWS",
  403. AccountID: "usageAccountID",
  404. AccountName: "usageAccountID",
  405. InvoiceEntityID: "payerAccountID",
  406. InvoiceEntityName: "payerAccountID",
  407. RegionID: "regionCode",
  408. AvailabilityZone: "availabilityZone",
  409. Service: "productCode",
  410. Category: opencost.OtherCategory,
  411. Labels: opencost.CloudCostLabels{
  412. "test": "userTagTestValue",
  413. "aws_test": "awsTagTestValue",
  414. },
  415. },
  416. Window: opencost.NewClosedWindow(
  417. time.Date(2024, 9, 1, 0, 0, 0, 0, time.UTC),
  418. time.Date(2024, 9, 2, 0, 0, 0, 0, time.UTC),
  419. ),
  420. ListCost: opencost.CostMetric{
  421. Cost: 1,
  422. KubernetesPercent: 1,
  423. },
  424. NetCost: opencost.CostMetric{
  425. Cost: 2,
  426. KubernetesPercent: 1,
  427. },
  428. AmortizedNetCost: opencost.CostMetric{
  429. Cost: 3,
  430. KubernetesPercent: 1,
  431. },
  432. InvoicedCost: opencost.CostMetric{
  433. Cost: 2,
  434. KubernetesPercent: 1,
  435. },
  436. AmortizedCost: opencost.CostMetric{
  437. Cost: 4,
  438. KubernetesPercent: 1,
  439. },
  440. },
  441. wantErr: false,
  442. },
  443. }
  444. for _, tt := range tests {
  445. t.Run(tt.name, func(t *testing.T) {
  446. row := stringsToRow(tt.row)
  447. got, err := athenaRowToCloudCost(row, tt.aqi)
  448. if (err != nil) != tt.wantErr {
  449. t.Errorf("RowToCloudCost() error = %v, wantErr %v", err, tt.wantErr)
  450. return
  451. }
  452. if !reflect.DeepEqual(got, tt.want) {
  453. t.Errorf("RowToCloudCost() got = %v, want %v", got, tt.want)
  454. }
  455. })
  456. }
  457. }
  458. func stringsToRow(strings []string) types.Row {
  459. var data []types.Datum
  460. for _, str := range strings {
  461. varChar := str
  462. data = append(data, types.Datum{VarCharValue: &varChar})
  463. }
  464. return types.Row{Data: data}
  465. }
  466. func TestAthenaIntegration_GetPartitionWhere(t *testing.T) {
  467. testCases := map[string]struct {
  468. integration *AthenaIntegration
  469. start time.Time
  470. end time.Time
  471. resourceTagsColumn bool
  472. expected string
  473. }{
  474. "CUR 1.0 single month": {
  475. integration: &AthenaIntegration{
  476. AthenaQuerier: AthenaQuerier{
  477. AthenaConfiguration: AthenaConfiguration{
  478. Bucket: "bucket",
  479. Region: "region",
  480. Database: "database",
  481. Table: "table",
  482. Workgroup: "workgroup",
  483. Account: "account",
  484. Authorizer: &ServiceAccount{},
  485. },
  486. },
  487. },
  488. start: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
  489. end: time.Date(2024, 1, 25, 0, 0, 0, 0, time.UTC),
  490. resourceTagsColumn: false,
  491. expected: "((year = '2024' AND month = '1'))",
  492. },
  493. "CUR 2.0 single month": {
  494. integration: &AthenaIntegration{
  495. AthenaQuerier: AthenaQuerier{
  496. AthenaConfiguration: AthenaConfiguration{
  497. Bucket: "bucket",
  498. Region: "region",
  499. Database: "database",
  500. Table: "table",
  501. Workgroup: "workgroup",
  502. Account: "account",
  503. Authorizer: &ServiceAccount{},
  504. },
  505. },
  506. },
  507. start: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
  508. end: time.Date(2024, 1, 25, 0, 0, 0, 0, time.UTC),
  509. resourceTagsColumn: true,
  510. expected: "((billing_period = '2024-01'))",
  511. },
  512. "CUR 1.0 multiple months": {
  513. integration: &AthenaIntegration{
  514. AthenaQuerier: AthenaQuerier{
  515. AthenaConfiguration: AthenaConfiguration{
  516. Bucket: "bucket",
  517. Region: "region",
  518. Database: "database",
  519. Table: "table",
  520. Workgroup: "workgroup",
  521. Account: "account",
  522. Authorizer: &ServiceAccount{},
  523. },
  524. },
  525. },
  526. start: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
  527. end: time.Date(2024, 3, 10, 0, 0, 0, 0, time.UTC),
  528. resourceTagsColumn: false,
  529. expected: "((year = '2024' AND month = '1') OR (year = '2024' AND month = '2') OR (year = '2024' AND month = '3'))",
  530. },
  531. "CUR 2.0 multiple months": {
  532. integration: &AthenaIntegration{
  533. AthenaQuerier: AthenaQuerier{
  534. AthenaConfiguration: AthenaConfiguration{
  535. Bucket: "bucket",
  536. Region: "region",
  537. Database: "database",
  538. Table: "table",
  539. Workgroup: "workgroup",
  540. Account: "account",
  541. Authorizer: &ServiceAccount{},
  542. },
  543. },
  544. },
  545. start: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
  546. end: time.Date(2024, 3, 10, 0, 0, 0, 0, time.UTC),
  547. resourceTagsColumn: true,
  548. expected: "((billing_period = '2024-01') OR (billing_period = '2024-02') OR (billing_period = '2024-03'))",
  549. },
  550. "CUR 2.0 across year boundary": {
  551. integration: &AthenaIntegration{
  552. AthenaQuerier: AthenaQuerier{
  553. AthenaConfiguration: AthenaConfiguration{
  554. Bucket: "bucket",
  555. Region: "region",
  556. Database: "database",
  557. Table: "table",
  558. Workgroup: "workgroup",
  559. Account: "account",
  560. Authorizer: &ServiceAccount{},
  561. },
  562. },
  563. },
  564. start: time.Date(2023, 12, 15, 0, 0, 0, 0, time.UTC),
  565. end: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
  566. resourceTagsColumn: true,
  567. expected: "((billing_period = '2023-12') OR (billing_period = '2024-01') OR (billing_period = '2024-02'))",
  568. },
  569. "CUR 1.0 across year boundary": {
  570. integration: &AthenaIntegration{
  571. AthenaQuerier: AthenaQuerier{
  572. AthenaConfiguration: AthenaConfiguration{
  573. Bucket: "bucket",
  574. Region: "region",
  575. Database: "database",
  576. Table: "table",
  577. Workgroup: "workgroup",
  578. Account: "account",
  579. Authorizer: &ServiceAccount{},
  580. },
  581. },
  582. },
  583. start: time.Date(2023, 12, 15, 0, 0, 0, 0, time.UTC),
  584. end: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
  585. resourceTagsColumn: false,
  586. expected: "((year = '2023' AND month = '12') OR (year = '2024' AND month = '1') OR (year = '2024' AND month = '2'))",
  587. },
  588. }
  589. for name, testCase := range testCases {
  590. t.Run(name, func(t *testing.T) {
  591. actual := testCase.integration.GetPartitionWhere(testCase.start, testCase.end, testCase.resourceTagsColumn)
  592. if actual != testCase.expected {
  593. t.Errorf("GetPartitionWhere() mismatch:\nActual: %s\nExpected: %s", actual, testCase.expected)
  594. }
  595. })
  596. }
  597. }