From 632a218a520a92dd414b3d4b808503a31f3eca9d Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Mon, 2 Aug 2021 14:20:42 +0800 Subject: [PATCH] Remove sourcemap/test and elasticsearch/estest elasticsearch/estest was only used inside the sourcemap package's tests. Remove this package and refactor tests to use an httptest.Server where feasible. Change the beater test that was using sourcemap/test for test data to use the sourcemap in testdata/sourcemap instead, and remove sourcemap/test. --- beater/beater_test.go | 8 +- elasticsearch/estest/client.go | 74 --------------- sourcemap/es_store_test.go | 169 +++++++++++++++++++++++++++------ sourcemap/mapper_test.go | 6 +- sourcemap/processor_test.go | 32 +++---- sourcemap/store_test.go | 43 ++++++--- sourcemap/test/es_client.go | 167 -------------------------------- 7 files changed, 190 insertions(+), 309 deletions(-) delete mode 100644 elasticsearch/estest/client.go delete mode 100644 sourcemap/test/es_client.go diff --git a/beater/beater_test.go b/beater/beater_test.go index a0e08238da6..4c099226b36 100644 --- a/beater/beater_test.go +++ b/beater/beater_test.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "io/ioutil" "net" "net/http" "net/http/httptest" @@ -40,7 +41,6 @@ import ( "github.com/elastic/apm-server/beater/config" "github.com/elastic/apm-server/elasticsearch" "github.com/elastic/apm-server/model" - "github.com/elastic/apm-server/sourcemap/test" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/instrumentation" @@ -268,11 +268,13 @@ func TestTransformConfigIndex(t *testing.T) { t.Run("with-observer-version", func(t *testing.T) { test(t, "blah-%{[observer.version]}-blah", "blah-1.2.3-blah") }) } +var validSourcemap, _ = ioutil.ReadFile("../testdata/sourcemap/bundle.js.map") + func TestStoreUsesRUMElasticsearchConfig(t *testing.T) { var called bool ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true - w.Write([]byte(test.ValidSourcemap)) + w.Write(validSourcemap) })) defer ts.Close() @@ -298,7 +300,7 @@ func TestFleetStoreUsed(t *testing.T) { called = true wr := zlib.NewWriter(w) defer wr.Close() - wr.Write([]byte(fmt.Sprintf(`{"sourceMap":%s}`, test.ValidSourcemap))) + wr.Write([]byte(fmt.Sprintf(`{"sourceMap":%s}`, validSourcemap))) })) defer ts.Close() diff --git a/elasticsearch/estest/client.go b/elasticsearch/estest/client.go deleted file mode 100644 index a49702afcc0..00000000000 --- a/elasticsearch/estest/client.go +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package estest - -import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" - "net/http" - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - - "github.com/elastic/apm-server/elasticsearch" -) - -// Transport can be used to pass to test Elasticsearch Client for more control over client behavior -type Transport struct { - roundTripFn func(req *http.Request) (*http.Response, error) - executed int -} - -// RoundTrip implements http.RoundTripper interface -func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - t.executed++ - return t.roundTripFn(req) -} - -// NewTransport creates test transport instance returning status code and body according to input parameters when called. -func NewTransport(t *testing.T, statusCode int, esBody map[string]interface{}) *Transport { - - return &Transport{ - roundTripFn: func(_ *http.Request) (*http.Response, error) { - if statusCode == -1 { - return nil, errors.New("client error") - } - var body io.ReadCloser - if esBody == nil { - body = ioutil.NopCloser(bytes.NewReader([]byte{})) - } else { - resp, err := json.Marshal(esBody) - require.NoError(t, err) - body = ioutil.NopCloser(bytes.NewReader(resp)) - } - return &http.Response{ - StatusCode: statusCode, - Body: body, - Header: http.Header{"X-Elastic-Product": []string{"Elasticsearch"}}, - }, nil - }, - } -} - -// NewElasticsearchClient creates ES client using the given transport instance -func NewElasticsearchClient(transport *Transport) (elasticsearch.Client, error) { - return elasticsearch.NewVersionedClient("", "", "", []string{}, nil, transport, 3, elasticsearch.DefaultBackoff) -} diff --git a/sourcemap/es_store_test.go b/sourcemap/es_store_test.go index 20a5a313f89..17a91860e25 100644 --- a/sourcemap/es_store_test.go +++ b/sourcemap/es_store_test.go @@ -18,55 +18,60 @@ package sourcemap import ( + "bytes" "context" + "encoding/json" + "io" "net/http" + "net/http/httptest" "testing" + "time" "github.com/go-sourcemap/sourcemap" - + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/apm-server/elasticsearch" - - "github.com/elastic/apm-server/elasticsearch/estest" logs "github.com/elastic/apm-server/log" - "github.com/elastic/apm-server/sourcemap/test" ) func Test_esFetcher_fetchError(t *testing.T) { for name, tc := range map[string]struct { - statusCode int - esBody map[string]interface{} - temporary bool + statusCode int + clientError bool + responseBody io.Reader + temporary bool }{ "es not reachable": { - statusCode: -1, temporary: true, + clientError: true, + temporary: true, }, "es bad request": { statusCode: http.StatusBadRequest, }, "empty sourcemap string": { - esBody: map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 1}, - "hits": []map[string]interface{}{ - {"_source": map[string]interface{}{ - "sourcemap": map[string]interface{}{ - "sourcemap": ""}}}}}}, + statusCode: http.StatusOK, + responseBody: sourcemapSearchResponseBody(1, []map[string]interface{}{{ + "_source": map[string]interface{}{ + "sourcemap": map[string]interface{}{ + "sourcemap": "", + }, + }, + }}), }, } { t.Run(name, func(t *testing.T) { - statusCode := tc.statusCode - if statusCode == 0 { - statusCode = http.StatusOK + var client elasticsearch.Client + if tc.clientError { + client = newUnavailableElasticsearchClient(t) + } else { + client = newMockElasticsearchClient(t, tc.statusCode, tc.responseBody) } - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, statusCode, tc.esBody)) - require.NoError(t, err) + consumer, err := testESStore(client).fetch(context.Background(), "abc", "1.0", "/tmp") - require.Error(t, err) if tc.temporary { assert.Contains(t, err.Error(), errMsgESFailure) } else { @@ -79,15 +84,27 @@ func Test_esFetcher_fetchError(t *testing.T) { func Test_esFetcher_fetch(t *testing.T) { for name, tc := range map[string]struct { - client elasticsearch.Client - filePath string + statusCode int + responseBody io.Reader + filePath string }{ - "no sourcemap found": {client: test.ESClientWithSourcemapNotFound(t)}, - "sourcemap indicated but not found": {client: test.ESClientWithSourcemapIndicatedNotFound(t)}, - "valid sourcemap found": {client: test.ESClientWithValidSourcemap(t), filePath: "bundle.js"}, + "no sourcemap found": { + statusCode: http.StatusNotFound, + responseBody: sourcemapSearchResponseBody(0, nil), + }, + "sourcemap indicated but not found": { + statusCode: http.StatusOK, + responseBody: sourcemapSearchResponseBody(1, []map[string]interface{}{}), + }, + "valid sourcemap found": { + statusCode: http.StatusOK, + responseBody: sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(validSourcemap)}), + filePath: "bundle.js", + }, } { t.Run(name, func(t *testing.T) { - sourcemapStr, err := testESStore(tc.client).fetch(context.Background(), "abc", "1.0", "/tmp") + client := newMockElasticsearchClient(t, tc.statusCode, tc.responseBody) + sourcemapStr, err := testESStore(client).fetch(context.Background(), "abc", "1.0", "/tmp") require.NoError(t, err) if tc.filePath == "" { @@ -104,3 +121,101 @@ func Test_esFetcher_fetch(t *testing.T) { func testESStore(client elasticsearch.Client) *esStore { return &esStore{client: client, index: "apm-sourcemap", logger: logp.NewLogger(logs.Sourcemap)} } + +func sourcemapSearchResponseBody(hitsTotal int, hits []map[string]interface{}) io.Reader { + resultHits := map[string]interface{}{ + "total": map[string]interface{}{ + "value": hitsTotal, + }, + } + if hits != nil { + resultHits["hits"] = hits + } + result := map[string]interface{}{"hits": resultHits} + data, err := json.Marshal(result) + if err != nil { + panic(err) + } + return bytes.NewReader(data) +} + +func sourcemapHit(sourcemap string) map[string]interface{} { + return map[string]interface{}{ + "_source": map[string]interface{}{ + "sourcemap": map[string]interface{}{ + "sourcemap": sourcemap, + }, + }, + } +} + +// newUnavailableElasticsearchClient returns an elasticsearch.Client configured +// to send requests to an invalid (unavailable) host. +func newUnavailableElasticsearchClient(t testing.TB) elasticsearch.Client { + var transport roundTripperFunc = func(r *http.Request) (*http.Response, error) { + return nil, errors.New("client error") + } + backoff := func(int) time.Duration { return 0 } + client, err := elasticsearch.NewVersionedClient("", "", "", []string{"testing.invalid"}, nil, transport, 1, backoff) + require.NoError(t, err) + return client +} + +// newMockElasticsearchClient returns an elasticsearch.Clien configured to send +// requests to an httptest.Server that responds to source map search requests +// with the given status code and response body. +func newMockElasticsearchClient(t testing.TB, statusCode int, responseBody io.Reader) elasticsearch.Client { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(statusCode) + if responseBody != nil { + io.Copy(w, responseBody) + } + })) + t.Cleanup(srv.Close) + config := elasticsearch.DefaultConfig() + config.Backoff.Init = time.Nanosecond + config.Hosts = []string{srv.URL} + client, err := elasticsearch.NewClient(config) + require.NoError(t, err) + return client +} + +// validSourcemap is an example of a valid sourcemap for use in tests. +const validSourcemap = `{ + "version": 3, + "sources": [ + "webpack:///bundle.js", + "", + "webpack:///./scripts/index.js", + "webpack:///./index.html", + "webpack:///./scripts/app.js" + ], + "names": [ + "modules", + "__webpack_require__", + "moduleId", + "installedModules", + "exports", + "module", + "id", + "loaded", + "call", + "m", + "c", + "p", + "foo", + "console", + "log", + "foobar" + ], + "mappings": "CAAS,SAAUA,GCInB,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAE,OAGA,IAAAC,GAAAF,EAAAD,IACAE,WACAE,GAAAJ,EACAK,QAAA,EAUA,OANAP,GAAAE,GAAAM,KAAAH,EAAAD,QAAAC,IAAAD,QAAAH,GAGAI,EAAAE,QAAA,EAGAF,EAAAD,QAvBA,GAAAD,KAqCA,OATAF,GAAAQ,EAAAT,EAGAC,EAAAS,EAAAP,EAGAF,EAAAU,EAAA,GAGAV,EAAA,KDMM,SAASI,EAAQD,EAASH,GE3ChCA,EAAA,GAEAA,EAAA,GAEAW,OFmDM,SAASP,EAAQD,EAASH,GGxDhCI,EAAAD,QAAAH,EAAAU,EAAA,cH8DM,SAASN,EAAQD,GI9DvB,QAAAQ,KACAC,QAAAC,IAAAC,QAGAH", + "file": "bundle.js", + "sourcesContent": [ + "/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t// Webpack\n\t__webpack_require__(1)\n\t\n\t__webpack_require__(2)\n\t\n\tfoo()\n\n\n/***/ },\n/* 1 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tmodule.exports = __webpack_require__.p + \"index.html\"\n\n/***/ },\n/* 2 */\n/***/ function(module, exports) {\n\n\tfunction foo() {\n\t console.log(foobar)\n\t}\n\t\n\tfoo()\n\n\n/***/ }\n/******/ ]);\n\n\n/** WEBPACK FOOTER **\n ** bundle.js\n **/", + " \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 6002740481c9666b0d38\n **/", + "// Webpack\nrequire('../index.html')\n\nrequire('./app')\n\nfoo()\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./scripts/index.js\n ** module id = 0\n ** module chunks = 0\n **/", + "module.exports = __webpack_public_path__ + \"index.html\"\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./index.html\n ** module id = 1\n ** module chunks = 0\n **/", + "function foo() {\n console.log(foobar)\n}\n\nfoo()\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./scripts/app.js\n ** module id = 2\n ** module chunks = 0\n **/" + ], + "sourceRoot": "" +}` diff --git a/sourcemap/mapper_test.go b/sourcemap/mapper_test.go index e3959ec653a..1a9f56f24ee 100644 --- a/sourcemap/mapper_test.go +++ b/sourcemap/mapper_test.go @@ -24,8 +24,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/elastic/apm-server/sourcemap/test" - "github.com/go-sourcemap/sourcemap" "github.com/stretchr/testify/assert" ) @@ -37,7 +35,7 @@ func TestMapNilConsumer(t *testing.T) { } func TestMapNoMatch(t *testing.T) { - m, err := sourcemap.Parse("", []byte(test.ValidSourcemap)) + m, err := sourcemap.Parse("", []byte(validSourcemap)) require.NoError(t, err) // nothing found for lineno and colno @@ -51,8 +49,6 @@ func TestMapNoMatch(t *testing.T) { } func TestMapMatch(t *testing.T) { - validSourcemap := test.ValidSourcemap - // Re-encode the sourcemap, adding carriage returns to the // line endings in the source content. decoded := make(map[string]interface{}) diff --git a/sourcemap/processor_test.go b/sourcemap/processor_test.go index 88fa1d7626c..07a1bcc75fc 100644 --- a/sourcemap/processor_test.go +++ b/sourcemap/processor_test.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package sourcemap_test +package sourcemap import ( "context" @@ -30,13 +30,13 @@ import ( "github.com/elastic/apm-server/elasticsearch" "github.com/elastic/apm-server/model" - "github.com/elastic/apm-server/sourcemap" - "github.com/elastic/apm-server/sourcemap/test" ) func TestBatchProcessor(t *testing.T) { - client := test.ESClientWithValidSourcemap(t) - store, err := sourcemap.NewElasticsearchStore(client, "index", time.Minute) + client := newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(string(validSourcemap))}), + ) + store, err := NewElasticsearchStore(client, "index", time.Minute) require.NoError(t, err) originalLinenoWithFilename := 1 @@ -183,7 +183,7 @@ func TestBatchProcessor(t *testing.T) { }, } - processor := sourcemap.BatchProcessor{Store: store} + processor := BatchProcessor{Store: store} err = processor.ProcessBatch(context.Background(), &model.Batch{transaction, span1, span2, error1, error2, error3}) assert.NoError(t, err) @@ -218,8 +218,8 @@ func TestBatchProcessor(t *testing.T) { } func TestBatchProcessorElasticsearchUnavailable(t *testing.T) { - client := test.ESClientUnavailable(t) - store, err := sourcemap.NewElasticsearchStore(client, "index", time.Minute) + client := newUnavailableElasticsearchClient(t) + store, err := NewElasticsearchStore(client, "index", time.Minute) require.NoError(t, err) nonMatchingFrame := model.StacktraceFrame{ @@ -241,7 +241,7 @@ func TestBatchProcessorElasticsearchUnavailable(t *testing.T) { logp.DevelopmentSetup(logp.ToObserverOutput()) for i := 0; i < 2; i++ { - processor := sourcemap.BatchProcessor{Store: store} + processor := BatchProcessor{Store: store} err = processor.ProcessBatch(context.Background(), &model.Batch{span, span}) assert.NoError(t, err) } @@ -260,19 +260,11 @@ func TestBatchProcessorElasticsearchUnavailable(t *testing.T) { func TestBatchProcessorTimeout(t *testing.T) { var transport roundTripperFunc = func(req *http.Request) (*http.Response, error) { <-req.Context().Done() - // TODO(axw) remove this "notTimeout" error wrapper when - // https://github.com/elastic/go-elasticsearch/issues/300 - // is fixed. - // - // Because context.DeadlineExceeded implements net.Error, - // go-elasticsearch continues retrying and does not exit - // early. - type notTimeout struct{ error } - return nil, notTimeout{req.Context().Err()} + return nil, req.Context().Err() } client, err := elasticsearch.NewVersionedClient("", "", "", []string{""}, nil, transport, 3, elasticsearch.DefaultBackoff) require.NoError(t, err) - store, err := sourcemap.NewElasticsearchStore(client, "index", time.Minute) + store, err := NewElasticsearchStore(client, "index", time.Minute) require.NoError(t, err) frame := model.StacktraceFrame{ @@ -292,7 +284,7 @@ func TestBatchProcessorTimeout(t *testing.T) { } before := time.Now() - processor := sourcemap.BatchProcessor{Store: store, Timeout: 100 * time.Millisecond} + processor := BatchProcessor{Store: store, Timeout: 100 * time.Millisecond} err = processor.ProcessBatch(context.Background(), &model.Batch{span}) assert.NoError(t, err) taken := time.Since(before) diff --git a/sourcemap/store_test.go b/sourcemap/store_test.go index 014167505d6..7cd6cd1e752 100644 --- a/sourcemap/store_test.go +++ b/sourcemap/store_test.go @@ -38,10 +38,18 @@ import ( "github.com/elastic/apm-server/elasticsearch" logs "github.com/elastic/apm-server/log" "github.com/elastic/beats/v7/libbeat/logp" - - "github.com/elastic/apm-server/sourcemap/test" ) +var unsupportedVersionSourcemap = `{ + "version": 1, + "sources": ["webpack:///bundle.js"], + "names": [], + "mappings": "CAAS", + "file": "bundle.js", + "sourcesContent": [], + "sourceRoot": "" +}` + func Test_newStore(t *testing.T) { logger := logp.NewLogger(logs.Sourcemap) @@ -60,7 +68,9 @@ func TestStore_Fetch(t *testing.T) { t.Run("cache", func(t *testing.T) { t.Run("nil", func(t *testing.T) { var nilConsumer *sourcemap.Consumer - store := testStore(t, test.ESClientWithValidSourcemap(t)) //if ES was queried, it would return a valid sourcemap + store := testStore(t, newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(validSourcemap)}), + )) store.add(key, nilConsumer) mapper, err := store.Fetch(context.Background(), serviceName, serviceVersion, path) @@ -70,7 +80,7 @@ func TestStore_Fetch(t *testing.T) { t.Run("sourcemapConsumer", func(t *testing.T) { consumer := &sourcemap.Consumer{} - store := testStore(t, test.ESClientUnavailable(t)) //if ES was queried, it would return a server error + store := testStore(t, newUnavailableElasticsearchClient(t)) store.add(key, consumer) mapper, err := store.Fetch(context.Background(), serviceName, serviceVersion, path) @@ -81,7 +91,9 @@ func TestStore_Fetch(t *testing.T) { }) t.Run("validFromES", func(t *testing.T) { - store := testStore(t, test.ESClientWithValidSourcemap(t)) + store := testStore(t, newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(validSourcemap)}), + )) mapper, err := store.Fetch(context.Background(), serviceName, serviceVersion, path) require.NoError(t, err) require.NotNil(t, mapper) @@ -93,8 +105,7 @@ func TestStore_Fetch(t *testing.T) { }) t.Run("notFoundInES", func(t *testing.T) { - - store := testStore(t, test.ESClientWithSourcemapNotFound(t)) + store := testStore(t, newMockElasticsearchClient(t, http.StatusNotFound, sourcemapSearchResponseBody(0, nil))) //not cached cached, found := store.cache.Get(key) require.False(t, found) @@ -113,8 +124,12 @@ func TestStore_Fetch(t *testing.T) { t.Run("invalidFromES", func(t *testing.T) { for name, client := range map[string]elasticsearch.Client{ - "invalid": test.ESClientWithInvalidSourcemap(t), - "unsupportedVersion": test.ESClientWithUnsupportedSourcemap(t), + "invalid": newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit("foo")}), + ), + "unsupportedVersion": newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(unsupportedVersionSourcemap)}), + ), } { t.Run(name, func(t *testing.T) { store := testStore(t, client) @@ -137,7 +152,7 @@ func TestStore_Fetch(t *testing.T) { }) t.Run("noConnectionToES", func(t *testing.T) { - store := testStore(t, test.ESClientUnavailable(t)) + store := testStore(t, newUnavailableElasticsearchClient(t)) //not cached _, found := store.cache.Get(key) require.False(t, found) @@ -234,7 +249,7 @@ func TestConcurrentFetch(t *testing.T) { version = "1.0.0" path = "/my/path/to/bundle.js.map" c = http.DefaultClient - res = fmt.Sprintf(`{"sourceMap":%s}`, test.ValidSourcemap) + res = fmt.Sprintf(`{"sourceMap":%s}`, validSourcemap) errsLeft = tc.errWant ) @@ -300,7 +315,9 @@ func TestStore_Added(t *testing.T) { // setup // remove empty sourcemap from cache, and valid one with File() == "bundle.js" from Elasticsearch - store := testStore(t, test.ESClientWithValidSourcemap(t)) + store := testStore(t, newMockElasticsearchClient(t, http.StatusOK, + sourcemapSearchResponseBody(1, []map[string]interface{}{sourcemapHit(validSourcemap)}), + )) store.add(key, &sourcemap.Consumer{}) mapper, err := store.Fetch(context.Background(), name, version, path) @@ -317,7 +334,7 @@ func TestStore_Added(t *testing.T) { } func TestExpiration(t *testing.T) { - store := testStore(t, test.ESClientUnavailable(t)) //if ES was queried it would return an error + store := testStore(t, newUnavailableElasticsearchClient(t)) //if ES was queried it would return an error store.cache = gocache.New(25*time.Millisecond, 100) store.add("foo_1.0.1_/tmp", &sourcemap.Consumer{}) name, version, path := "foo", "1.0.1", "/tmp" diff --git a/sourcemap/test/es_client.go b/sourcemap/test/es_client.go deleted file mode 100644 index f10e4e004fe..00000000000 --- a/sourcemap/test/es_client.go +++ /dev/null @@ -1,167 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package test - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/elastic/apm-server/elasticsearch" - - "github.com/elastic/apm-server/elasticsearch/estest" -) - -//ValidSourcemap represents an example for a valid sourcemap string -var ValidSourcemap = `{ - "version": 3, - "sources": [ - "webpack:///bundle.js", - "", - "webpack:///./scripts/index.js", - "webpack:///./index.html", - "webpack:///./scripts/app.js" - ], - "names": [ - "modules", - "__webpack_require__", - "moduleId", - "installedModules", - "exports", - "module", - "id", - "loaded", - "call", - "m", - "c", - "p", - "foo", - "console", - "log", - "foobar" - ], - "mappings": "CAAS,SAAUA,GCInB,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAE,OAGA,IAAAC,GAAAF,EAAAD,IACAE,WACAE,GAAAJ,EACAK,QAAA,EAUA,OANAP,GAAAE,GAAAM,KAAAH,EAAAD,QAAAC,IAAAD,QAAAH,GAGAI,EAAAE,QAAA,EAGAF,EAAAD,QAvBA,GAAAD,KAqCA,OATAF,GAAAQ,EAAAT,EAGAC,EAAAS,EAAAP,EAGAF,EAAAU,EAAA,GAGAV,EAAA,KDMM,SAASI,EAAQD,EAASH,GE3ChCA,EAAA,GAEAA,EAAA,GAEAW,OFmDM,SAASP,EAAQD,EAASH,GGxDhCI,EAAAD,QAAAH,EAAAU,EAAA,cH8DM,SAASN,EAAQD,GI9DvB,QAAAQ,KACAC,QAAAC,IAAAC,QAGAH", - "file": "bundle.js", - "sourcesContent": [ - "/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t// Webpack\n\t__webpack_require__(1)\n\t\n\t__webpack_require__(2)\n\t\n\tfoo()\n\n\n/***/ },\n/* 1 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tmodule.exports = __webpack_require__.p + \"index.html\"\n\n/***/ },\n/* 2 */\n/***/ function(module, exports) {\n\n\tfunction foo() {\n\t console.log(foobar)\n\t}\n\t\n\tfoo()\n\n\n/***/ }\n/******/ ]);\n\n\n/** WEBPACK FOOTER **\n ** bundle.js\n **/", - " \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 6002740481c9666b0d38\n **/", - "// Webpack\nrequire('../index.html')\n\nrequire('./app')\n\nfoo()\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./scripts/index.js\n ** module id = 0\n ** module chunks = 0\n **/", - "module.exports = __webpack_public_path__ + \"index.html\"\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./index.html\n ** module id = 1\n ** module chunks = 0\n **/", - "function foo() {\n console.log(foobar)\n}\n\nfoo()\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./scripts/app.js\n ** module id = 2\n ** module chunks = 0\n **/" - ], - "sourceRoot": "" -}` - -// ESClientWithValidSourcemap returns an elasticsearch client that will always return a document containing -// a valid sourcemap. -func ESClientWithValidSourcemap(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, http.StatusOK, validSourcemapFromES())) - require.NoError(t, err) - return client -} - -// ESClientUnavailable returns an elasticsearch client that will always return a client error, mimicking an -// unavailable Elasticsearch server. -func ESClientUnavailable(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, -1, nil)) - require.NoError(t, err) - return client -} - -// ESClientWithInvalidSourcemap returns an elasticsearch client that will always return a document containing -// an invalid sourcemap. -func ESClientWithInvalidSourcemap(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, http.StatusOK, invalidSourcemapFromES())) - require.NoError(t, err) - return client -} - -// ESClientWithUnsupportedSourcemap returns an elasticsearch client that will always return a document containing -// a sourcemap with an unsupported version. -func ESClientWithUnsupportedSourcemap(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, http.StatusOK, sourcemapUnsupportedVersionFromES())) - require.NoError(t, err) - return client -} - -// ESClientWithSourcemapNotFound returns an elasticsearch client that will always return a not found error -func ESClientWithSourcemapNotFound(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, http.StatusNotFound, sourcemapNotFoundFromES())) - require.NoError(t, err) - return client -} - -// ESClientWithSourcemapIndicatedNotFound returns an elasticsearch client that will always return a result indicating -// a sourcemap exists but it actually doesn't contain it. This is an edge case that usually shouldn't happen. -func ESClientWithSourcemapIndicatedNotFound(t *testing.T) elasticsearch.Client { - client, err := estest.NewElasticsearchClient(estest.NewTransport(t, http.StatusOK, sourcemapIndicatedNotFoundFromES())) - require.NoError(t, err) - return client -} - -func validSourcemapFromES() map[string]interface{} { - return map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 1}, - "hits": []map[string]interface{}{ - {"_source": map[string]interface{}{ - "sourcemap": map[string]interface{}{ - "sourcemap": ValidSourcemap}}}}}} -} - -func sourcemapNotFoundFromES() map[string]interface{} { - return map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 0}}} -} - -func sourcemapIndicatedNotFoundFromES() map[string]interface{} { - return map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 1}, - "hits": []map[string]interface{}{}}} -} - -func invalidSourcemapFromES() map[string]interface{} { - return map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 1}, - "hits": []map[string]interface{}{ - {"_source": map[string]interface{}{ - "sourcemap": map[string]interface{}{ - "sourcemap": "foo"}}}}}} -} - -func sourcemapUnsupportedVersionFromES() map[string]interface{} { - return map[string]interface{}{ - "hits": map[string]interface{}{ - "total": map[string]interface{}{"value": 1}, - "hits": []map[string]interface{}{ - {"_source": map[string]interface{}{ - "sourcemap": map[string]interface{}{ - "sourcemap": `{ - "version": 1, - "sources": ["webpack:///bundle.js"], - "names": [], - "mappings": "CAAS", - "file": "bundle.js", - "sourcesContent": [], - "sourceRoot": "" - }`}}}}}} -}