| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | package elasticsearch | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/experimental" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // these snapshot-tests test the whole request-response flow:
 | 
					
						
							|  |  |  | // the inputs:
 | 
					
						
							|  |  |  | // - the backend.DataQuery query
 | 
					
						
							|  |  |  | // - the elastic-response json
 | 
					
						
							|  |  |  | // the snapshot verifies:
 | 
					
						
							|  |  |  | // - the elastic-request json
 | 
					
						
							|  |  |  | // - the dataframe result
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 17:39:58 +08:00
										 |  |  | // If you need to adjust the snapshots, go to Line 172 and change
 | 
					
						
							|  |  |  | // `experimental.CheckGoldenJSONResponse(t, "testdata_response", goldenFileName, &dataResCopy, false)` to `experimental.CheckGoldenJSONResponse(t, "testdata_response", goldenFileName, &dataResCopy, true)`
 | 
					
						
							|  |  |  | // then run the test once to generate the new snapshots.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | // a regex that matches the request-snapshot-filenames, and extracts the name of the test
 | 
					
						
							|  |  |  | var requestRe = regexp.MustCompile(`^(.*)\.request\.line\d+\.json$`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // the "elastic request" is often in multiple json-snapshot-files,
 | 
					
						
							|  |  |  | // so we have to find them on disk, so we have to look at every file in
 | 
					
						
							|  |  |  | // the folder.
 | 
					
						
							|  |  |  | func findRequestSnapshots(t *testing.T, folder string) map[string][]string { | 
					
						
							|  |  |  | 	allTestSnapshotFiles, err := os.ReadDir(folder) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snapshots := make(map[string][]string) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, file := range allTestSnapshotFiles { | 
					
						
							|  |  |  | 		fileName := file.Name() | 
					
						
							|  |  |  | 		match := requestRe.FindStringSubmatch(fileName) | 
					
						
							|  |  |  | 		if len(match) == 2 { | 
					
						
							|  |  |  | 			testName := match[1] | 
					
						
							|  |  |  | 			files := append(snapshots[testName], filepath.Join(folder, fileName)) | 
					
						
							|  |  |  | 			snapshots[testName] = files | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return snapshots | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // a regex that matches the response-snapshot-filenames, and extracts the name of the test
 | 
					
						
							|  |  |  | var responseRe = regexp.MustCompile(`^([^\.]+)\.[^\.]+.golden.jsonc$`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func findResponseSnapshotCounts(t *testing.T, folder string) map[string]int { | 
					
						
							|  |  |  | 	allTestSnapshotFiles, err := os.ReadDir(folder) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snapshots := make(map[string]int) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, file := range allTestSnapshotFiles { | 
					
						
							|  |  |  | 		fileName := file.Name() | 
					
						
							|  |  |  | 		match := responseRe.FindStringSubmatch(fileName) | 
					
						
							|  |  |  | 		if len(match) == 2 { | 
					
						
							|  |  |  | 			testName := match[1] | 
					
						
							|  |  |  | 			snapshots[testName] = snapshots[testName] + 1 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return snapshots | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestRequestSnapshots(t *testing.T) { | 
					
						
							|  |  |  | 	tt := []struct { | 
					
						
							|  |  |  | 		name string | 
					
						
							|  |  |  | 		path string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{name: "simple metric test", path: "metric_simple"}, | 
					
						
							|  |  |  | 		{name: "complex metric test", path: "metric_complex"}, | 
					
						
							|  |  |  | 		{name: "multi metric test", path: "metric_multi"}, | 
					
						
							| 
									
										
										
										
											2022-12-14 20:56:09 +08:00
										 |  |  | 		{name: "raw data test", path: "raw_data"}, | 
					
						
							|  |  |  | 		{name: "raw document test", path: "raw_document"}, | 
					
						
							|  |  |  | 		{name: "logs test", path: "logs"}, | 
					
						
							| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	queryHeader := []byte(` | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		"ignore_unavailable": true, | 
					
						
							| 
									
										
										
										
											2023-07-11 15:47:16 +08:00
										 |  |  | 		"index": "testdb-2022.11.14", | 
					
						
							| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | 		"search_type": "query_then_fetch" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	requestSnapshots := findRequestSnapshots(t, "testdata_request") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tt { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			responseBytes := []byte(`{"responses":[]}`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			queriesFileName := filepath.Join("testdata_request", test.path+".queries.json") | 
					
						
							|  |  |  | 			queriesBytes, err := os.ReadFile(filepath.Clean(queriesFileName)) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var requestLines [][]byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for _, fileName := range requestSnapshots[test.path] { | 
					
						
							|  |  |  | 				bytes, err := os.ReadFile(filepath.Clean(fileName)) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				requestLines = append(requestLines, bytes) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			require.True(t, len(requestLines) > 0, "requestLines must not be empty") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			result, err := queryDataTest(queriesBytes, responseBytes) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reqLines := strings.Split(strings.TrimSpace(string(result.requestBytes)), "\n") | 
					
						
							|  |  |  | 			require.Len(t, reqLines, len(requestLines)*2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for i, expectedRequestLine := range requestLines { | 
					
						
							|  |  |  | 				actualRequestHeaderLine := reqLines[2*i] | 
					
						
							|  |  |  | 				actualRequestLine := reqLines[2*i+1] | 
					
						
							|  |  |  | 				require.JSONEq(t, string(queryHeader), actualRequestHeaderLine, fmt.Sprintf("invalid request-header at index: %v", i)) | 
					
						
							|  |  |  | 				require.JSONEq(t, string(expectedRequestLine), actualRequestLine, fmt.Sprintf("invalid request at index: %v", i)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestResponseSnapshots(t *testing.T) { | 
					
						
							|  |  |  | 	tt := []struct { | 
					
						
							|  |  |  | 		name string | 
					
						
							|  |  |  | 		path string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{name: "simple metric test", path: "metric_simple"}, | 
					
						
							|  |  |  | 		{name: "complex metric test", path: "metric_complex"}, | 
					
						
							|  |  |  | 		{name: "multi metric test", path: "metric_multi"}, | 
					
						
							| 
									
										
										
										
											2023-03-20 21:50:26 +08:00
										 |  |  | 		{name: "metric avg test", path: "metric_avg"}, | 
					
						
							|  |  |  | 		{name: "metric percentiles test", path: "metric_percentiles"}, | 
					
						
							|  |  |  | 		{name: "metric top_metrics test", path: "metric_top_metrics"}, | 
					
						
							|  |  |  | 		{name: "metric extended_stats test", path: "metric_extended_stats"}, | 
					
						
							| 
									
										
										
										
											2023-02-22 20:28:43 +08:00
										 |  |  | 		{name: "raw data test", path: "raw_data"}, | 
					
						
							| 
									
										
										
										
											2023-03-01 18:50:56 +08:00
										 |  |  | 		{name: "logs test", path: "logs"}, | 
					
						
							| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snapshotCount := findResponseSnapshotCounts(t, "testdata_response") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tt { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			responseFileName := filepath.Join("testdata_response", test.path+".response.json") | 
					
						
							|  |  |  | 			responseBytes, err := os.ReadFile(filepath.Clean(responseFileName)) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			queriesFileName := filepath.Join("testdata_response", test.path+".queries.json") | 
					
						
							|  |  |  | 			queriesBytes, err := os.ReadFile(filepath.Clean(queriesFileName)) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			result, err := queryDataTest(queriesBytes, responseBytes) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// first we need to test that the number of items in `result.response.Responses`,
 | 
					
						
							|  |  |  | 			// is exactly the same as the count of our response snapshot files
 | 
					
						
							|  |  |  | 			// (this is so that we avoid situations where we provide more snapshot-files than
 | 
					
						
							|  |  |  | 			// what is returned)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			expectedResponseCount := snapshotCount[test.path] | 
					
						
							|  |  |  | 			require.True(t, expectedResponseCount > 0, "response snapshots not found") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			require.Len(t, result.response.Responses, expectedResponseCount) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for refId, dataRes := range result.response.Responses { | 
					
						
							| 
									
										
										
										
											2023-03-20 21:50:26 +08:00
										 |  |  | 				goldenFileName := fmt.Sprintf("%v.%v.golden", test.path, strings.ToLower(refId)) | 
					
						
							| 
									
										
										
										
											2022-11-28 21:59:57 +08:00
										 |  |  | 				// we make a copy of the variable to avoid this linter-warning:
 | 
					
						
							|  |  |  | 				// "G601: Implicit memory aliasing in for loop."
 | 
					
						
							|  |  |  | 				dataResCopy := dataRes | 
					
						
							|  |  |  | 				experimental.CheckGoldenJSONResponse(t, "testdata_response", goldenFileName, &dataResCopy, false) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |