From 957529e901115a16ed31c4663e460e4044d9a09d Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Wed, 20 Dec 2023 11:12:50 -0500 Subject: [PATCH] test: Refactored tests that were still using the tap mocha shim + chai to now use tap. (#1919) --- THIRD_PARTY_NOTICES.md | 258 ++-- package-lock.json | 133 -- package.json | 1 - .../collector-remote-method.tap.js | 5 +- test/integration/instrumentation/fetch.tap.js | 12 +- .../promises/legacy-promise-segments.js | 33 +- .../instrumentation/promises/segments.js | 33 +- test/lib/agent_helper.js | 1119 ++++++++--------- test/lib/custom-assertions.js | 81 ++ test/lib/logging-helper.js | 5 +- test/lib/metrics_helper.js | 71 +- test/unit/agent/intrinsics.test.js | 60 +- test/unit/apdex.test.js | 80 +- .../api-start-background-transaction.test.js | 1 - test/unit/api/api-start-segment.test.js | 2 - .../api/api-start-web-transaction.test.js | 1 - test/unit/assert-metrics.test.js | 129 -- test/unit/collector/remote-method.test.js | 11 +- test/unit/collector/serverless.test.js | 4 +- .../custom-event-aggregator.test.js | 33 +- test/unit/db/trace.test.js | 163 ++- test/unit/db_util.test.js | 85 +- test/unit/distributed_tracing/dt-cats.test.js | 48 +- .../distributed_tracing/dt-payload.test.js | 50 +- .../distributed_tracing/tracecontext.test.js | 853 +++++++------ test/unit/header-attributes.test.js | 199 +-- test/unit/header-processing.test.js | 72 +- test/unit/metric/datastore-instance.test.js | 45 +- test/unit/metric/metric-aggregator.test.js | 577 +++++---- test/unit/metric/metrics.test.js | 526 ++++---- test/unit/metric/normalizer-rule.test.js | 274 ++-- test/unit/metric/normalizer.test.js | 366 +++--- test/unit/metrics-mapper.test.js | 87 +- .../distributed-trace.test.js | 152 +-- test/unit/metrics-recorder/generic.test.js | 109 +- .../metrics-recorder/http-external.test.js | 126 +- test/unit/metrics-recorder/http.test.js | 513 ++++---- .../metrics-recorder/queue-time-http.test.js | 6 +- test/unit/name-state.test.js | 66 +- test/unit/rum.test.js | 254 ++-- test/unit/sampler.test.js | 212 ++-- test/unit/shim/message-shim.test.js | 3 - test/unit/shim/shim.test.js | 2 - test/unit/shim/transaction-shim.test.js | 2 - test/unit/shimmer.test.js | 466 +++---- test/unit/spans/span-event.test.js | 7 +- test/unit/stats.test.js | 110 +- test/unit/test-helpers/metrics_helper.test.js | 105 -- test/unit/timer.test.js | 147 ++- test/unit/trace-aggregator.test.js | 443 ++++--- test/unit/tracer.test.js | 136 +- .../unit/transaction-event-aggregator.test.js | 263 ++-- test/unit/urltils.test.js | 415 +++--- test/unit/util/code-level-metrics.test.js | 23 +- test/versioned/amqplib/amqp-utils.js | 88 +- test/versioned/bunyan/bunyan.tap.js | 4 +- test/versioned/express-esm/segments.tap.mjs | 73 +- .../express/client-disconnect.tap.js | 30 +- test/versioned/express/segments.tap.js | 11 +- test/versioned/fastify/add-hook.tap.js | 6 +- .../fastify/code-level-metrics-hooks.tap.js | 2 - .../code-level-metrics-middleware.tap.js | 2 - test/versioned/fastify/naming-common.js | 4 +- test/versioned/grpc/util.cjs | 9 +- test/versioned/hapi/segments.tap.js | 20 +- test/versioned/ioredis/ioredis-3.tap.js | 4 +- test/versioned/ioredis/ioredis.tap.js | 9 +- test/versioned/openai/chat-completions.tap.js | 20 +- test/versioned/openai/common.js | 21 +- test/versioned/openai/embeddings.tap.js | 10 +- test/versioned/pino/pino.tap.js | 39 +- test/versioned/prisma/utils.js | 4 +- .../restify-post-7/async-handlers.tap.js | 4 +- .../restify/restify-post-7/restify.tap.js | 4 +- .../restify/restify-pre-7/restify.tap.js | 4 +- test/versioned/undici/requests.tap.js | 20 +- test/versioned/winston/winston.tap.js | 4 +- third_party_manifest.json | 25 +- 78 files changed, 4672 insertions(+), 4722 deletions(-) create mode 100644 test/lib/custom-assertions.js delete mode 100644 test/unit/assert-metrics.test.js delete mode 100644 test/unit/test-helpers/metrics_helper.test.js diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index b98bbb66bb..05100dc7e8 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -42,7 +42,6 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic * [ajv](#ajv) * [async](#async) * [c8](#c8) -* [chai](#chai) * [clean-jsdoc-theme](#clean-jsdoc-theme) * [commander](#commander) * [conventional-changelog-conventionalcommits](#conventional-changelog-conventionalcommits) @@ -1263,7 +1262,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ### import-in-the-middle -This product includes source derived from [import-in-the-middle](https://github.com/DataDog/import-in-the-middle) ([v1.4.2](https://github.com/DataDog/import-in-the-middle/tree/v1.4.2)), distributed under the [Apache-2.0 License](https://github.com/DataDog/import-in-the-middle/blob/v1.4.2/LICENSE): +This product includes source derived from [import-in-the-middle](https://github.com/DataDog/import-in-the-middle) ([v1.6.0](https://github.com/DataDog/import-in-the-middle/tree/v1.6.0)), distributed under the [Apache-2.0 License](https://github.com/DataDog/import-in-the-middle/blob/v1.6.0/LICENSE): ``` Copyright 2021 Datadog, Inc. @@ -2268,61 +2267,32 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ``` -### chai - -This product includes source derived from [chai](https://github.com/chaijs/chai) ([v4.3.8](https://github.com/chaijs/chai/tree/v4.3.8)), distributed under the [MIT License](https://github.com/chaijs/chai/blob/v4.3.8/LICENSE): - -``` -MIT License - -Copyright (c) 2017 Chai.js Assertion Library - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - ### clean-jsdoc-theme This product includes source derived from [clean-jsdoc-theme](https://github.com/ankitskvmdam/clean-jsdoc-theme) ([v4.2.10](https://github.com/ankitskvmdam/clean-jsdoc-theme/tree/v4.2.10)), distributed under the [MIT License](https://github.com/ankitskvmdam/clean-jsdoc-theme/blob/v4.2.10/LICENSE): ``` -MIT License - -Copyright (c) 2019-2022 Ankit Kumar (अंकित कुमार) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2019-2022 Ankit Kumar (अंकित कुमार) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ``` @@ -4375,13 +4345,13 @@ License: MIT By: DY Repository: -> The MIT License (MIT) -> Copyright (c) 2015 Dmitry Ivanov -> -> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -> +> The MIT License (MIT) +> Copyright (c) 2015 Dmitry Ivanov +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------- @@ -8615,17 +8585,17 @@ License: 0BSD By: Microsoft Corp. Repository: -> Copyright (c) Microsoft Corporation. -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -> AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +> Copyright (c) Microsoft Corporation. +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +> AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR > PERFORMANCE OF THIS SOFTWARE. ---------------------------------------- @@ -8636,26 +8606,26 @@ License: MIT By: Klaus Meinhardt Repository: -> The MIT License (MIT) -> -> Copyright (c) 2017 Klaus Meinhardt -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> The MIT License (MIT) +> +> Copyright (c) 2017 Klaus Meinhardt +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE > SOFTWARE. ---------------------------------------- @@ -8666,60 +8636,60 @@ License: Apache-2.0 By: Microsoft Corp. Repository: -> Apache License -> -> Version 2.0, January 2004 -> -> http://www.apache.org/licenses/ -> -> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -> -> 1. Definitions. -> -> "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -> -> "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -> -> "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -> -> "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -> -> "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -> -> "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. -> -> "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). -> -> "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. -> -> "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." -> -> "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. -> -> 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. -> -> 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. -> -> 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: -> -> You must give any other recipients of the Work or Derivative Works a copy of this License; and -> -> You must cause any modified files to carry prominent notices stating that You changed the files; and -> -> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -> -> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -> -> 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. -> -> 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. -> -> 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. -> -> 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. -> -> 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. -> +> Apache License +> +> Version 2.0, January 2004 +> +> http://www.apache.org/licenses/ +> +> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +> +> 1. Definitions. +> +> "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +> +> "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +> +> "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +> +> "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +> +> "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +> +> "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +> +> "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +> +> "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +> +> "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +> +> "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +> +> 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +> +> 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +> +> 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +> +> You must give any other recipients of the Work or Derivative Works a copy of this License; and +> +> You must cause any modified files to carry prominent notices stating that You changed the files; and +> +> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +> +> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +> +> 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +> +> 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +> +> 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +> +> 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +> +> 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +> > END OF TERMS AND CONDITIONS ---------------------------------------- diff --git a/package-lock.json b/package-lock.json index be0935355f..e67f6e60f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,6 @@ "ajv": "^6.12.6", "async": "^3.2.4", "c8": "^8.0.1", - "chai": "^4.1.2", "clean-jsdoc-theme": "^4.2.4", "commander": "^7.0.0", "conventional-changelog-conventionalcommits": "^5.0.0", @@ -5248,15 +5247,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -5800,24 +5790,6 @@ "node": ">= 10" } }, - "node_modules/chai": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", - "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5840,15 +5812,6 @@ "node": ">=16" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -6560,18 +6523,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -7992,15 +7943,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -10311,15 +10253,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -11652,15 +11585,6 @@ "node": ">=8" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -20492,12 +20416,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -20892,21 +20810,6 @@ "lodash": "^4.17.15" } }, - "chai": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", - "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -20923,12 +20826,6 @@ "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz", "integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==" }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true - }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -21471,15 +21368,6 @@ } } }, - "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -22542,12 +22430,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true - }, "get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -24287,15 +24169,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, - "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -25301,12 +25174,6 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", diff --git a/package.json b/package.json index 8e1070c6ef..37ea1d5068 100644 --- a/package.json +++ b/package.json @@ -213,7 +213,6 @@ "ajv": "^6.12.6", "async": "^3.2.4", "c8": "^8.0.1", - "chai": "^4.1.2", "clean-jsdoc-theme": "^4.2.4", "commander": "^7.0.0", "conventional-changelog-conventionalcommits": "^5.0.0", diff --git a/test/integration/collector-remote-method.tap.js b/test/integration/collector-remote-method.tap.js index fbfec2aaa6..1729f3c1fa 100644 --- a/test/integration/collector-remote-method.tap.js +++ b/test/integration/collector-remote-method.tap.js @@ -13,7 +13,7 @@ const url = require('url') const collector = require('../lib/fake-collector') const RemoteMethod = require('../../lib/collector/remote-method') const NAMES = require('../../lib/metrics/names') -const { tapAssertMetrics } = require('../lib/metrics_helper') +require('../lib/metrics_helper') const { instrumentMockedAgent, unloadAgent } = require('../lib/agent_helper') const { SSL_HOST } = require('../lib/agent_helper') @@ -176,8 +176,7 @@ tap.test('record data usage supportability metrics', (t) => { method.invoke(payload, resolve) }) - tapAssertMetrics( - t, + t.assertMetricValues( { metrics: agent.metrics }, diff --git a/test/integration/instrumentation/fetch.tap.js b/test/integration/instrumentation/fetch.tap.js index 2321f6a403..3dbe1c4251 100644 --- a/test/integration/instrumentation/fetch.tap.js +++ b/test/integration/instrumentation/fetch.tap.js @@ -81,7 +81,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) }) t.equal(status, 200) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) tx.end() t.end() }) @@ -152,7 +152,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) const [{ status }, { status: status2 }] = await Promise.all([req1, req2]) t.equal(status, 200) t.equal(status2, 200) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { + t.assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { exact: false }) tx.end() @@ -168,7 +168,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) }) } catch (err) { t.equal(err.message, 'fetch failed') - metrics.assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) + t.assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) t.equal(tx.exceptions.length, 1) tx.end() t.end() @@ -188,7 +188,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) }, 100) await req } catch (err) { - metrics.assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) t.equal(tx.exceptions.length, 1) t.equal(tx.exceptions[0].error.name, 'AbortError') tx.end() @@ -215,7 +215,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) try { await req } catch (error) { - metrics.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { + t.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { exact: false }) @@ -236,7 +236,7 @@ tap.test('fetch', { skip: semver.lte(process.version, '18.0.0') }, function (t) helper.runInTransaction(agent, async (tx) => { const { status } = await fetch(`${REQUEST_URL}/status/400`) t.equal(status, 400) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) tx.end() t.end() }) diff --git a/test/integration/instrumentation/promises/legacy-promise-segments.js b/test/integration/instrumentation/promises/legacy-promise-segments.js index a1a8c24461..c80285a0c2 100644 --- a/test/integration/instrumentation/promises/legacy-promise-segments.js +++ b/test/integration/instrumentation/promises/legacy-promise-segments.js @@ -6,7 +6,7 @@ 'use strict' const helper = require('../../../lib/agent_helper') -const assertSegments = require('../../../lib/metrics_helper').assertSegments +require('../../../lib/metrics_helper') module.exports = runTests @@ -45,7 +45,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, [ + t.assertSegments(tx.trace.root, [ 'doSomeWork', ['Promise startSomeWork', ['Promise#then ', ['someChildSegment']]] ]) @@ -72,7 +72,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1', ['doWork2', ['secondThen']]]) + t.assertSegments(tx.trace.root, ['doWork1', ['doWork2', ['secondThen']]]) t.end() }) @@ -97,7 +97,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, [ + t.assertSegments(tx.trace.root, [ 'doWork1', ['Promise startSomeWork', ['Promise#then firstThen', ['Promise#then secondThen']]] ]) @@ -122,7 +122,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, [ + t.assertSegments(tx.trace.root, [ 'doWork1', ['Promise startSomeWork', ['Promise#catch catchHandler']] ]) @@ -150,7 +150,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1', ['doWork2', ['catchHandler']]]) + t.assertSegments(tx.trace.root, ['doWork1', ['doWork2', ['catchHandler']]]) t.end() }) @@ -180,8 +180,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 2) - checkSegments( - t, + t.assertSegments( tx.trace.root, ['Promise startSomeWork', ['Promise#then myThen'], 'doSomeWork'], true @@ -220,7 +219,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doSomeWork', ['someChildSegment']]) + t.assertSegments(tx.trace.root, ['doSomeWork', ['someChildSegment']]) t.end() }) @@ -242,7 +241,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -266,7 +265,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -288,7 +287,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -310,7 +309,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1', ['doWork2']]) + t.assertSegments(tx.trace.root, ['doWork1', ['doWork2']]) t.end() }) @@ -333,7 +332,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doSomeWork'], true) + t.assertSegments(tx.trace.root, ['doSomeWork'], true) t.end() }) @@ -358,9 +357,3 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { }) }) } - -function checkSegments(t, parent, expected, options) { - t.doesNotThrow(function () { - assertSegments(parent, expected, options) - }, 'should have expected segments') -} diff --git a/test/integration/instrumentation/promises/segments.js b/test/integration/instrumentation/promises/segments.js index bfd3fe3f0c..d12b3d80ea 100644 --- a/test/integration/instrumentation/promises/segments.js +++ b/test/integration/instrumentation/promises/segments.js @@ -6,7 +6,8 @@ 'use strict' const helper = require('../../../lib/agent_helper') -const assertSegments = require('../../../lib/metrics_helper').assertSegments +// load the assertSegments assertion +require('../../../lib/metrics_helper') module.exports = runTests @@ -43,7 +44,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 2) - checkSegments(t, tx.trace.root, ['doSomeWork', 'someChildSegment']) + t.assertSegments(tx.trace.root, ['doSomeWork', 'someChildSegment']) t.end() }) @@ -62,7 +63,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { t.test('segments: then handler that returns a new promise', function (t) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 3) - checkSegments(t, tx.trace.root, ['doWork1', 'doWork2', 'secondThen']) + t.assertSegments(tx.trace.root, ['doWork1', 'doWork2', 'secondThen']) t.end() }) @@ -85,7 +86,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -105,7 +106,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -124,7 +125,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { t.test('segments: catch handler with error from subsequent promise', function (t) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 3) - checkSegments(t, tx.trace.root, ['doWork1', 'doWork2', 'catchHandler']) + t.assertSegments(tx.trace.root, ['doWork1', 'doWork2', 'catchHandler']) t.end() }) @@ -152,7 +153,7 @@ function segmentsEnabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doSomeWork'], true) + t.assertSegments(tx.trace.root, ['doSomeWork'], true) t.end() }) @@ -185,7 +186,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 2) - checkSegments(t, tx.trace.root, ['doSomeWork', 'someChildSegment']) + t.assertSegments(tx.trace.root, ['doSomeWork', 'someChildSegment']) t.end() }) @@ -205,7 +206,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -227,7 +228,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -247,7 +248,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doWork1']) + t.assertSegments(tx.trace.root, ['doWork1']) t.end() }) @@ -267,7 +268,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 2) - checkSegments(t, tx.trace.root, ['doWork1', 'doWork2']) + t.assertSegments(tx.trace.root, ['doWork1', 'doWork2']) t.end() }) @@ -288,7 +289,7 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { agent.once('transactionFinished', function (tx) { t.equal(tx.trace.root.children.length, 1) - checkSegments(t, tx.trace.root, ['doSomeWork'], true) + t.assertSegments(tx.trace.root, ['doSomeWork'], true) t.end() }) @@ -313,9 +314,3 @@ function segmentsDisabledTests(t, agent, Promise, doSomeWork) { }) }) } - -function checkSegments(t, parent, expected, options) { - t.doesNotThrow(function () { - assertSegments(parent, expected, options) - }, 'should have expected segments') -} diff --git a/test/lib/agent_helper.js b/test/lib/agent_helper.js index 614b8eddff..b9cca168d6 100644 --- a/test/lib/agent_helper.js +++ b/test/lib/agent_helper.js @@ -27,16 +27,10 @@ const CAPATH = path.join(__dirname, 'ca-certificate.crt') let _agent = null let _agentApi = null const tasks = [] +// Load custom tap assertions +require('./custom-assertions') -/** - * Set up an unref'd loop to execute tasks that are added - * via helper.runOutOfContext - */ -const outOfContextQueueInterval = setInterval(() => { - while (tasks.length) { - tasks.pop()() - } -}, 25).unref() +const helper = module.exports function FakeTx() {} @@ -44,7 +38,7 @@ FakeTx.prototype.getFullName = function () { return this.name } -function FakeTransaction(agent, url = null) { +helper.FakeTransaction = function FakeTransaction(agent, url = null) { let transaction = {} if (agent) { transaction = new Transaction(agent) @@ -58,7 +52,7 @@ function FakeTransaction(agent, url = null) { return transaction } -function FakeSegment(transaction, duration, name = 'FakeSegment') { +helper.FakeSegment = function FakeSegment(transaction, duration, name = 'FakeSegment') { this.transaction = transaction this.attributes = {} this.name = name @@ -71,639 +65,572 @@ function FakeSegment(transaction, duration, name = 'FakeSegment') { } } -const helper = (module.exports = { - SSL_HOST: 'localhost', - outOfContextQueueInterval, - FakeSegment, - FakeTransaction, - getAgent: () => _agent, - getContextManager: () => _agent && _agent._contextManager, - - /** - * Set up an agent that won't try to connect to the collector, but also - * won't instrument any calling code. - * - * @param {object} conf Any configuration to override in the agent. - * See agent.js for details, but so far this includes - * passing in a config object and the connection stub - * created in this function. - * @returns {Agent} Agent with a stubbed configuration. - */ - loadMockedAgent: (conf, setState = true) => { - if (_agent) { - throw _agent.__created - } +helper.SSL_HOST = 'localhost' +helper.getAgent = () => _agent +helper.getContextManager = () => _agent && _agent._contextManager - // agent needs a 'real' configuration - const configurator = require('../../lib/config') - const config = configurator.createInstance(conf) +/** + * Set up an agent that won't try to connect to the collector, but also + * won't instrument any calling code. + * + * @param {object} conf Any configuration to override in the agent. + * See agent.js for details, but so far this includes + * passing in a config object and the connection stub + * created in this function. + * @returns {Agent} Agent with a stubbed configuration. + */ +helper.loadMockedAgent = function loadMockedAgent(conf, setState = true) { + if (_agent) { + throw _agent.__created + } - if (!config.debug) { - config.debug = {} - } + // agent needs a 'real' configuration + const configurator = require('../../lib/config') + const config = configurator.createInstance(conf) - // adds link to parents node in traces for easier testing - config.debug.double_linked_transactions = true + if (!config.debug) { + config.debug = {} + } - // stub applications - config.applications = () => ['New Relic for Node.js tests'] + // adds link to parents node in traces for easier testing + config.debug.double_linked_transactions = true - _agent = new Agent(config) - _agent.__created = new Error('Only one agent at a time! This one was created at:') - _agent.recordSupportability = () => {} // Stub supportabilities. + // stub applications + config.applications = () => ['New Relic for Node.js tests'] - if (setState) { - _agent.setState('started') - } + _agent = new Agent(config) + _agent.__created = new Error('Only one agent at a time! This one was created at:') + _agent.recordSupportability = () => {} // Stub supportabilities. - return _agent - }, - getAgentApi: function getAgentApi() { - // TODO: this needs moar safety, maybe different style based on how this helper is compared to test utils - if (!_agentApi) { - _agentApi = new API(_agent) - } + if (setState) { + _agent.setState('started') + } - return _agentApi - }, - - /** - * Generate the URLs used to talk to the collector, which have a very - * specific format. Useful with nock. - * - * @param {String} method The method being invoked on the collector. - * @param number runID Agent run ID (optional). - * - * @returns {String} URL path for the collector. - */ - generateCollectorPath: (method, runID, protocolVersion) => { - protocolVersion = protocolVersion || 17 - let fragment = - '/agent_listener/invoke_raw_method?' + - `marshal_format=json&protocol_version=${protocolVersion}&` + - `license_key=license%20key%20here&method=${method}` - - if (runID) { - fragment += '&run_id=' + runID - } + return _agent +} - return fragment - }, - - generateAllPaths: (runId) => { - return { - CONNECT: helper.generateCollectorPath('connect'), - CUSTOM_EVENTS: helper.generateCollectorPath('custom_event_data', runId), - ERRORS: helper.generateCollectorPath('error_data', runId), - ERROR_EVENTS: helper.generateCollectorPath('error_event_data', runId), - EVENTS: helper.generateCollectorPath('analytic_event_data', runId), - LOGS: helper.generateCollectorPath('log_event_data', runId), - METRICS: helper.generateCollectorPath('metric_data', runId), - PRECONNECT: helper.generateCollectorPath('preconnect'), - QUERIES: helper.generateCollectorPath('sql_trace_data', runId), - SETTINGS: helper.generateCollectorPath('agent_settings', runId), - SHUTDOWN: helper.generateCollectorPath('shutdown', runId), - SPAN_EVENTS: helper.generateCollectorPath('span_event_data', runId), - TRACES: helper.generateCollectorPath('transaction_sample_data', runId) - } - }, - - /** - * Builds on loadMockedAgent by patching the module loader and setting up - * the instrumentation framework. - * - * @param {object} conf - * Any configuration to override in the agent. See agent.js for details, - * but so far this includes passing in a config object and the connection - * stub created in this function. - * - * @param {boolean} [setState=true] - * Initializes agent's state to 'started', enabling data collection. - * - * @returns {Agent} Agent with a stubbed configuration. - */ - instrumentMockedAgent: (conf, setState = true, shimmer = require('../../lib/shimmer')) => { - shimmer.debug = true - - const agent = helper.loadMockedAgent(conf, setState) - - shimmer.bootstrapInstrumentation(agent) - shimmer.registerHooks(agent) - helper.maybeLoadSecurityAgent(agent) - - return agent - }, - - /** - * Helper to check if security agent should be loaded - * - * @param {Agent} Agent with a stubbed configuration - * @returns {boolean} - */ - isSecurityAgentEnabled(agent) { - return agent.config?.security?.agent?.enabled - }, - - /** - * Checks if security agent _should_ be loaded - * and requires it and calls start - * - * @param {Agent} Agent with a stubbed configuration - */ - maybeLoadSecurityAgent(agent) { - if (helper.isSecurityAgentEnabled(agent)) { - agent.config.security.enabled = true - const api = helper.getAgentApi(agent) - require('@newrelic/security-agent').start(api) - } - }, - - /** - * Checks if security agent is loaded and deletes all - * files in its require cache so it can be re-loaded - * - * @param {Agent} Agent with a stubbed configuration - */ - maybeUnloadSecurityAgent(agent) { - if (helper.isSecurityAgentEnabled(agent)) { - Object.keys(require.cache).forEach((key) => { - if (key.includes('@newrelic/security-agent')) { - delete require.cache[key] - } - }) - } - }, - - /** - * Shut down the agent, ensuring that any instrumentation scaffolding - * is shut down. - * - * @param Agent agent The agent to shut down. - */ - unloadAgent: (agent, shimmer = require('../../lib/shimmer')) => { - agent.emit('unload') - shimmer.removeHooks() - shimmer.unwrapAll() - shimmer.registeredInstrumentations = Object.create(null) - shimmer.debug = false - helper.maybeUnloadSecurityAgent(agent) - - // Stop future harvesting by aggregators. - agent.stopAggregators() - - if (agent === _agent) { - _agent = null - _agentApi = null - } - }, +helper.getAgentApi = function getAgentApi() { + // TODO: this needs moar safety, maybe different style based on how this helper is compared to test utils + if (!_agentApi) { + _agentApi = new API(_agent) + } - loadTestAgent: (t, conf, setState = true) => { - const agent = helper.instrumentMockedAgent(conf, setState) - t.teardown(() => { - helper.unloadAgent(agent) - }) + return _agentApi +} - return agent - }, - - /** - * Create a transactional scope in which instrumentation that will only add - * trace segments to existing transactions will funciton. - * - * If the agent hasn't been started, set to a state that can collect transactions. - * - * @param {Agent} agent The agent whose tracer should be used to create the - * transaction. - * @param {Function} callback The function to be run within the transaction. - */ - runInTransaction: (agent, type, callback) => { - if (!callback && typeof type === 'function') { - callback = type - type = undefined - } - if (!(agent && callback)) { - throw new TypeError('Must include both agent and function!') - } - type = type || 'web' +/** + * Generate the URLs used to talk to the collector, which have a very + * specific format. Useful with nock. + * + * @param {String} method The method being invoked on the collector. + * @param number runID Agent run ID (optional). + * + * @returns {String} URL path for the collector. + */ +helper.generateCollectorPath = function generateCollectorPath(method, runID, protocolVersion) { + protocolVersion = protocolVersion || 17 + let fragment = + '/agent_listener/invoke_raw_method?' + + `marshal_format=json&protocol_version=${protocolVersion}&` + + `license_key=license%20key%20here&method=${method}` + + if (runID) { + fragment += '&run_id=' + runID + } - // if the agent hasn't been started, set to a state that can collect transactions. - // do not override states for an agent that is already started or in the - // process of starting. - if (agent._state === 'stopped') { - agent.setState('started') - } + return fragment +} - return agent.tracer.transactionNestProxy(type, () => { - const transaction = agent.getTransaction() - return callback(transaction) - })() // <-- invoke immediately - }, - - /** - * Proxy for runInTransaction that names the transaction that the - * callback is executed in - */ - runInNamedTransaction: (agent, type, callback) => { - if (!callback && typeof type === 'function') { - callback = type - type = undefined - } +helper.generateAllPaths = (runId) => { + return { + CONNECT: helper.generateCollectorPath('connect'), + CUSTOM_EVENTS: helper.generateCollectorPath('custom_event_data', runId), + ERRORS: helper.generateCollectorPath('error_data', runId), + ERROR_EVENTS: helper.generateCollectorPath('error_event_data', runId), + EVENTS: helper.generateCollectorPath('analytic_event_data', runId), + LOGS: helper.generateCollectorPath('log_event_data', runId), + METRICS: helper.generateCollectorPath('metric_data', runId), + PRECONNECT: helper.generateCollectorPath('preconnect'), + QUERIES: helper.generateCollectorPath('sql_trace_data', runId), + SETTINGS: helper.generateCollectorPath('agent_settings', runId), + SHUTDOWN: helper.generateCollectorPath('shutdown', runId), + SPAN_EVENTS: helper.generateCollectorPath('span_event_data', runId), + TRACES: helper.generateCollectorPath('transaction_sample_data', runId) + } +} - return helper.runInTransaction(agent, type, (transaction) => { - transaction.name = 'TestTransaction' - return callback(transaction) - }) - }, - - runInSegment: (agent, name, callback) => { - const tracer = agent.tracer - - return tracer.addSegment(name, null, null, null, callback) - }, - - /** - * Stub to bootstrap a memcached instance - * - * @param {Function} callback The operations to be performed while the server - * is running. - */ - bootstrapMemcached: (callback) => { - const Memcached = require('memcached') - const memcached = new Memcached(params.memcached_host + ':' + params.memcached_port) - memcached.flush((err) => { - memcached.end() - callback(err) - }) - }, - - /** - * Select Redis DB index and flush entries in it. - * - * @param {redis} [redis] - * @param {number} dbIndex - * @param {function} callback - * The operations to be performed while the server is running. - */ - flushRedisDb: (client, dbIndex) => { - return new Promise((resolve, reject) => { - client.select(dbIndex, (err) => { - if (err) { - client.end(true) - reject(err) - } +/** + * Builds on loadMockedAgent by patching the module loader and setting up + * the instrumentation framework. + * + * @param {object} conf + * Any configuration to override in the agent. See agent.js for details, + * but so far this includes passing in a config object and the connection + * stub created in this function. + * + * @param {boolean} [setState=true] + * Initializes agent's state to 'started', enabling data collection. + * + * @returns {Agent} Agent with a stubbed configuration. + */ +helper.instrumentMockedAgent = (conf, setState = true, shimmer = require('../../lib/shimmer')) => { + shimmer.debug = true - client.flushdb((err) => { - if (err) { - reject(err) - } + const agent = helper.loadMockedAgent(conf, setState) - resolve() - }) - }) - }) - }, - - withSSL: () => { - return Promise.all([fs.readFile(KEYPATH), fs.readFile(CERTPATH), fs.readFile(CAPATH)]) - }, - - // FIXME: I long for the day I no longer need this gross hack - onlyDomains: () => { - const exceptionHandlers = process._events.uncaughtException - if (exceptionHandlers) { - if (Array.isArray(exceptionHandlers)) { - process._events.uncaughtException = exceptionHandlers.filter((f) => { - return f.name === 'uncaughtHandler' - }) - } else if (exceptionHandlers.name !== 'uncaughtException') { - delete process._events.uncaughtException - } - } + shimmer.bootstrapInstrumentation(agent) + shimmer.registerHooks(agent) + helper.maybeLoadSecurityAgent(agent) - return exceptionHandlers - }, - - randomPort: (callback) => { - const net = require('net') - // Min port: 1024 (without root) - // Max port: 65535 - // Our range: 1024-65024 - const port = Math.ceil(Math.random() * 64000 + 1024) - const server = net - .createServer() - .once('listening', () => { - server.close(() => { - process.nextTick(callback.bind(null, port)) - }) - }) - .once('error', (err) => { - if (err.code === 'EADDRINUSE') { - helper.randomPort(callback) - } else { - throw err - } - }) - server.listen(port) - }, - - startServerWithRandomPortRetry: (server, maxAttempts = 5) => { - let attempts = 0 - server.on('error', (e) => { - // server port not guranteed to be not in use - if (e.code === 'EADDRINUSE') { - if (attempts >= maxAttempts) { - // eslint-disable-next-line no-console - console.log('Exceeded max attempts (%s), bailing out.', maxAttempts) - throw new Error('Unable to get unused port') - } + return agent +} - attempts++ +/** + * Helper to check if security agent should be loaded + * + * @param {Agent} Agent with a stubbed configuration + * @returns {boolean} + */ +helper.isSecurityAgentEnabled = function isSecurityAgentEnabled(agent) { + return agent.config?.security?.agent?.enabled +} - // eslint-disable-next-line no-console - console.log('Address in use, retrying...') - setTimeout(() => { - server.close() +/** + * Checks if security agent _should_ be loaded + * and requires it and calls start + * + * @param {Agent} Agent with a stubbed configuration + */ +helper.maybeLoadSecurityAgent = function maybeLoadSecurityAgent(agent) { + if (helper.isSecurityAgentEnabled(agent)) { + agent.config.security.enabled = true + const api = helper.getAgentApi(agent) + require('@newrelic/security-agent').start(api) + } +} - // start the server using a random port - server.listen() - }, 1000) +/** + * Checks if security agent is loaded and deletes all + * files in its require cache so it can be re-loaded + * + * @param {Agent} Agent with a stubbed configuration + */ +helper.maybeUnloadSecurityAgent = function maybeUnloadSecurityAgent(agent) { + if (helper.isSecurityAgentEnabled(agent)) { + Object.keys(require.cache).forEach((key) => { + if (key.includes('@newrelic/security-agent')) { + delete require.cache[key] } }) + } +} - server.listen() - }, - - /** - * Get the appropriate request method. - * If you pass in ca(certificate authority) we assume - * you want to make a https request. Also since this - * request is made after instrumentation is registered - * we want to make sure we get the original library and not - * our instrumented one - */ - getRequestLib(ca) { - const request = ca ? https.request : http.request - return request[symbols.original] || request - }, - - makeGetRequest: (url, options, callback) => { - helper.makeRequest(url, options, callback) - }, - - makeRequest: (url, options, callback) => { - if (!options || typeof options === 'function') { - callback = options - options = {} - } +/** + * Shut down the agent, ensuring that any instrumentation scaffolding + * is shut down. + * + * @param Agent agent The agent to shut down. + */ +helper.unloadAgent = (agent, shimmer = require('../../lib/shimmer')) => { + agent.emit('unload') + shimmer.removeHooks() + shimmer.unwrapAll() + shimmer.registeredInstrumentations = Object.create(null) + shimmer.debug = false + helper.maybeUnloadSecurityAgent(agent) + + // Stop future harvesting by aggregators. + agent.stopAggregators() + + if (agent === _agent) { + _agent = null + _agentApi = null + } +} - const request = helper.getRequestLib(options.ca) - const req = request(url, options, function requestCb(res) { - const contentType = res.headers['content-type'] - let rawData = '' +helper.loadTestAgent = (t, conf, setState = true) => { + const agent = helper.instrumentMockedAgent(conf, setState) + t.teardown(() => { + helper.unloadAgent(agent) + }) - res.on('data', (chunk) => { - rawData += chunk - }) + return agent +} + +/** + * Create a transactional scope in which instrumentation that will only add + * trace segments to existing transactions will funciton. + * + * If the agent hasn't been started, set to a state that can collect transactions. + * + * @param {Agent} agent The agent whose tracer should be used to create the + * transaction. + * @param {Function} callback The function to be run within the transaction. + */ +helper.runInTransaction = (agent, type, callback) => { + if (!callback && typeof type === 'function') { + callback = type + type = undefined + } + if (!(agent && callback)) { + throw new TypeError('Must include both agent and function!') + } + type = type || 'web' + + // if the agent hasn't been started, set to a state that can collect transactions. + // do not override states for an agent that is already started or in the + // process of starting. + if (agent._state === 'stopped') { + agent.setState('started') + } + + return agent.tracer.transactionNestProxy(type, () => { + const transaction = agent.getTransaction() + return callback(transaction) + })() // <-- invoke immediately +} + +/** + * Proxy for runInTransaction that names the transaction that the + * callback is executed in + */ +helper.runInNamedTransaction = (agent, type, callback) => { + if (!callback && typeof type === 'function') { + callback = type + type = undefined + } + + return helper.runInTransaction(agent, type, (transaction) => { + transaction.name = 'TestTransaction' + return callback(transaction) + }) +} + +helper.runInSegment = (agent, name, callback) => { + const tracer = agent.tracer + + return tracer.addSegment(name, null, null, null, callback) +} + +/** + * Stub to bootstrap a memcached instance + * + * @param {Function} callback The operations to be performed while the server + * is running. + */ +helper.bootstrapMemcached = (callback) => { + const Memcached = require('memcached') + const memcached = new Memcached(params.memcached_host + ':' + params.memcached_port) + memcached.flush((err) => { + memcached.end() + callback(err) + }) +} + +/** + * Select Redis DB index and flush entries in it. + * + * @param {redis} [redis] + * @param {number} dbIndex + * @param {function} callback + * The operations to be performed while the server is running. + */ +helper.flushRedisDb = (client, dbIndex) => { + return new Promise((resolve, reject) => { + client.select(dbIndex, (err) => { + if (err) { + client.end(true) + reject(err) + } - res.on('end', () => { - if (typeof callback === 'function') { - const body = contentType?.includes('application/json') ? JSON.parse(rawData) : rawData - // assign body to res as when this method is promisified it can only return 2 args: err, result - res.body = body - callback(null, res, body) + client.flushdb((err) => { + if (err) { + reject(err) } + + resolve() + }) + }) + }) +} + +helper.withSSL = () => { + return Promise.all([fs.readFile(KEYPATH), fs.readFile(CERTPATH), fs.readFile(CAPATH)]) +} + +helper.randomPort = (callback) => { + const net = require('net') + // Min port: 1024 (without root) + // Max port: 65535 + // Our range: 1024-65024 + const port = Math.ceil(Math.random() * 64000 + 1024) + const server = net + .createServer() + .once('listening', () => { + server.close(() => { + process.nextTick(callback.bind(null, port)) }) - }).on('error', (err) => { - if (err.code === 'ECONNREFUSED') { - this.makeGetRequest(url, options, callback) + }) + .once('error', (err) => { + if (err.code === 'EADDRINUSE') { + helper.randomPort(callback) } else { - callback(err) + throw err } }) + server.listen(port) +} - if (options.method === 'POST' && options.body) { - req.write(options.body) - } +helper.startServerWithRandomPortRetry = (server, maxAttempts = 5) => { + let attempts = 0 + server.on('error', (e) => { + // server port not guranteed to be not in use + if (e.code === 'EADDRINUSE') { + if (attempts >= maxAttempts) { + // eslint-disable-next-line no-console + console.log('Exceeded max attempts (%s), bailing out.', maxAttempts) + throw new Error('Unable to get unused port') + } - req.end() - }, + attempts++ - temporarilyRemoveListeners: (t, emitter, evnt) => { - if (!emitter) { - t.comment('Not removing %s listeners, emitter does not exist', evnt) - return - } + // eslint-disable-next-line no-console + console.log('Address in use, retrying...') + setTimeout(() => { + server.close() - t.comment('Removing listeners for %s', evnt) - let listeners = emitter.listeners(evnt) - t.teardown(() => { - t.comment('Re-adding listeners for %s', evnt) - listeners.forEach((fn) => { - emitter.on(evnt, fn) - }) - listeners = [] - }) - emitter.removeAllListeners(evnt) - }, - - /** - * Tap will prevent certain uncaughtException behaviors from occuring - * and adds extra properties. This bypasses that. - * While t.expectUncaughtException seems intended for a similar use case, - * it does not seem to work appropriately for some of our use casese. - */ - temporarilyOverrideTapUncaughtBehavior: (tap, t) => { - const originalThrew = tap.threw - // Prevent tap from failing test and remove extra prop - tap.threw = (err) => { - delete err.tapCaught + // start the server using a random port + server.listen() + }, 1000) } + }) - const originalTestThrew = t.threw - t.threw = (err) => { - delete err.tapCaught - } + server.listen() +} - t.teardown(() => { - t.threw = originalTestThrew - tap.threw = originalThrew - }) - }, - - /** - * Adds a function to the outOfContext interval - * above - * - * @param {Function} fn to execute - */ - runOutOfContext: function (fn) { - tasks.push(fn) - }, - - decodeServerlessPayload: (t, payload, cb) => { - if (!payload) { - t.comment('No payload to decode') - return cb() - } +/** + * Get the appropriate request method. + * If you pass in ca(certificate authority) we assume + * you want to make a https request. Also since this + * request is made after instrumentation is registered + * we want to make sure we get the original library and not + * our instrumented one + */ +helper.getRequestLib = function getRequestLib(ca) { + const request = ca ? https.request : http.request + return request[symbols.original] || request +} - zlib.gunzip(Buffer.from(payload, 'base64'), (err, decompressed) => { - if (err) { - t.comment('Error occurred when decompressing payload') - return cb(err) - } +helper.makeGetRequest = (url, options, callback) => { + helper.makeRequest(url, options, callback) +} - let parsed = null - try { - parsed = JSON.parse(decompressed) - cb(null, parsed) - } catch (err) { - cb(err) +helper.makeRequest = (url, options, callback) => { + if (!options || typeof options === 'function') { + callback = options + options = {} + } + + const request = helper.getRequestLib(options.ca) + const req = request(url, options, function requestCb(res) { + const contentType = res.headers['content-type'] + let rawData = '' + + res.on('data', (chunk) => { + rawData += chunk + }) + + res.on('end', () => { + if (typeof callback === 'function') { + const body = contentType?.includes('application/json') ? JSON.parse(rawData) : rawData + // assign body to res as when this method is promisified it can only return 2 args: err, result + res.body = body + callback(null, res, body) } }) - }, - - makeAttributeFilterConfig: (rules = {}) => { - rules = copy.shallow(rules, defaultAttributeConfig()) - return copy.shallow(rules, new EventEmitter()) - }, - - getMetrics(agent) { - return agent.metrics._metrics - }, - - /** - * to be used to extend tap assertions - * tap.Test.prototype.addAssert('isNonWritable', 1, isNonWritable) - * - * @param {Object} params - * @param {Object} params.obj obj to assign value - * @param {string} params.key key to assign value - * @param {string} params.value expected value of obj[key] - */ - isNonWritable({ obj, key, value }) { - this.throws(function () { - obj[key] = 'testNonWritable test value' - }, new RegExp("(read only property '" + key + "'|Cannot set property " + key + ')')) - - if (value) { - this.equal(obj[key], value) + }).on('error', (err) => { + if (err.code === 'ECONNREFUSED') { + this.makeGetRequest(url, options, callback) } else { - this.not(obj[key], 'testNonWritable test value', 'should not set value when non-writable') + callback(err) } - }, - /** - * Verifies the expected length of children segments and that every - * id matches between a segment array and the children - * - * @param {Object} parent trace - * @param {Array} segments list of expected segments - */ - compareSegments(parent, segments) { - this.ok(parent.children.length, segments.length, 'should be the same amount of children') - segments.forEach((segment, index) => { - this.equal(parent.children[index].id, segment.id, 'should have same ids') - }) - }, - /** - * Asserts the wrapped callback is wrapped and the unwrapped version is the original. - * It also verifies it does not throw an error - * - * @param {object} shim shim lib - * @param {Function} original callback - */ - checkWrappedCb(shim, cb) { - // The wrapped calledback is always the last argument - const wrappedCB = arguments[arguments.length - 1] - this.not(wrappedCB, cb) - this.ok(shim.isWrapped(wrappedCB)) - this.equal(shim.unwrap(wrappedCB), cb) - - this.doesNotThrow(function () { - wrappedCB() - }) + }) - this.end() - }, - /** - * Asserts the appropriate Code Level Metrics attributes on a segment - * - * @param {object} params - * @param {object} params.segments list of segments to assert { segment, filepath, name } - * @param {boolean} params.enabled if CLM is enabled or not - */ - assertCLMAttrs({ segments, enabled: clmEnabled }) { - segments.forEach((segment) => { - const attrs = segment.segment.getAttributes() - if (clmEnabled) { - this.equal(attrs['code.function'], segment.name, 'should have appropriate code.function') - this.ok( - attrs['code.filepath'].endsWith(segment.filepath), - 'should have appropriate code.filepath' - ) - this.match(attrs['code.lineno'], /[\d]+/, 'lineno should be a number') - this.match(attrs['code.column'], /[\d]+/, 'column should be a number') - } else { - this.notOk(attrs['code.function'], 'function should not exist') - this.notOk(attrs['code.filepath'], 'filepath should not exist') - this.notOk(attrs['code.lineno'], 'lineno should not exist') - this.notOk(attrs['code.column'], 'column should not exist') - } + if (options.method === 'POST' && options.body) { + req.write(options.body) + } + + req.end() +} + +helper.temporarilyRemoveListeners = (t, emitter, evnt) => { + if (!emitter) { + t.comment('Not removing %s listeners, emitter does not exist', evnt) + return + } + + t.comment('Removing listeners for %s', evnt) + let listeners = emitter.listeners(evnt) + t.teardown(() => { + t.comment('Re-adding listeners for %s', evnt) + listeners.forEach((fn) => { + emitter.on(evnt, fn) }) - }, - /** - * Unwraps one or more items, revealing the original value. - * - * - `unwrap(nodule, property)` - * - `unwrap(func)` - * - * If called with a `nodule` and properties, the unwrapped values will be put - * back on the nodule. Otherwise, the unwrapped function is just returned. - * - * @param {object | Function} nodule - * The source for the properties to unwrap, or a single function to unwrap. - * @param {string|Array.} [properties] - * One or more properties to unwrap. If omitted, the `nodule` parameter is - * assumed to be the function to unwrap. - * @returns {object | Function} The first parameter after unwrapping. - */ - unwrap(nodule, properties) { - // Don't try to unwrap potentially `null` or `undefined` things. - if (!nodule) { - return nodule - } + listeners = [] + }) + emitter.removeAllListeners(evnt) +} - // If we're unwrapping multiple things - if (Array.isArray(properties)) { - properties.forEach(module.exports.unwrap.bind(this, nodule)) - return nodule +/** + * Tap will prevent certain uncaughtException behaviors from occuring + * and adds extra properties. This bypasses that. + * While t.expectUncaughtException seems intended for a similar use case, + * it does not seem to work appropriately for some of our use casese. + */ +helper.temporarilyOverrideTapUncaughtBehavior = (tap, t) => { + const originalThrew = tap.threw + // Prevent tap from failing test and remove extra prop + tap.threw = (err) => { + delete err.tapCaught + } + + const originalTestThrew = t.threw + t.threw = (err) => { + delete err.tapCaught + } + + t.teardown(() => { + t.threw = originalTestThrew + tap.threw = originalThrew + }) +} + +/** + * Set up an unref'd loop to execute tasks that are added + * via helper.runOutOfContext + */ +helper.outOfContextQueueInterval = setInterval(() => { + while (tasks.length) { + tasks.pop()() + } +}, 25).unref() + +/** + * Adds a function to the outOfContext interval + * above + * + * @param {Function} fn to execute + */ +helper.runOutOfContext = function runOutOfContext(fn) { + tasks.push(fn) +} + +helper.decodeServerlessPayload = (t, payload, cb) => { + if (!payload) { + t.comment('No payload to decode') + return cb() + } + + zlib.gunzip(Buffer.from(payload, 'base64'), (err, decompressed) => { + if (err) { + t.comment('Error occurred when decompressing payload') + return cb(err) } - let original = properties ? nodule[properties] : nodule - while (original && original[symbols.original]) { - original = - original[symbols.unwrap] instanceof Function - ? original[symbols.unwrap]() - : original[symbols.original] + let parsed = null + try { + parsed = JSON.parse(decompressed) + cb(null, parsed) + } catch (err) { + cb(err) } - return original - }, - /** - * Util that checks if current node version is supported - * @param {string} version semver version string - * @returns {boolean} if version is supported - */ - isSupportedVersion(version) { - return semver.gt(process.version, version) - }, - - /** - * The https-proxy-server we support finally supports keep alive - * See: https://github.com/TooTallNate/proxy-agents/pull/147 - * In order for tap to shutdown we must destroy the https agent. - * This assumes the agent already exists as a singleton so we can destroy - * the active http agent - */ - destroyProxyAgent() { - require('../../lib/collector/http-agents').proxyAgent().destroy() + }) +} + +helper.makeAttributeFilterConfig = (rules = {}) => { + rules = copy.shallow(rules, defaultAttributeConfig()) + return copy.shallow(rules, new EventEmitter()) +} + +helper.getMetrics = function getMetrics(agent) { + return agent.metrics._metrics +} + +/** + * Asserts the wrapped callback is wrapped and the unwrapped version is the original. + * It also verifies it does not throw an error + * + * @param {object} shim shim lib + * @param {Function} original callback + */ +helper.checkWrappedCb = function checkWrappedCb(shim, cb) { + // The wrapped calledback is always the last argument + const wrappedCB = arguments[arguments.length - 1] + this.not(wrappedCB, cb) + this.ok(shim.isWrapped(wrappedCB)) + this.equal(shim.unwrap(wrappedCB), cb) + + this.doesNotThrow(function () { + wrappedCB() + }) + + this.end() +} + +/** + * Unwraps one or more items, revealing the original value. + * + * - `unwrap(nodule, property)` + * - `unwrap(func)` + * + * If called with a `nodule` and properties, the unwrapped values will be put + * back on the nodule. Otherwise, the unwrapped function is just returned. + * + * @param {object | Function} nodule + * The source for the properties to unwrap, or a single function to unwrap. + * @param {string|Array.} [properties] + * One or more properties to unwrap. If omitted, the `nodule` parameter is + * assumed to be the function to unwrap. + * @returns {object | Function} The first parameter after unwrapping. + */ +helper.unwrap = function unwrap(nodule, properties) { + // Don't try to unwrap potentially `null` or `undefined` things. + if (!nodule) { + return nodule + } + + // If we're unwrapping multiple things + if (Array.isArray(properties)) { + properties.forEach(module.exports.unwrap.bind(this, nodule)) + return nodule + } + + let original = properties ? nodule[properties] : nodule + while (original && original[symbols.original]) { + original = + original[symbols.unwrap] instanceof Function + ? original[symbols.unwrap]() + : original[symbols.original] } -}) + return original +} + +/** + * Util that checks if current node version is supported + * @param {string} version semver version string + * @returns {boolean} if version is supported + */ +helper.isSupportedVersion = function isSupportedVersion(version) { + return semver.gt(process.version, version) +} + +/** + * The https-proxy-server we support finally supports keep alive + * See: https://github.com/TooTallNate/proxy-agents/pull/147 + * In order for tap to shutdown we must destroy the https agent. + * This assumes the agent already exists as a singleton so we can destroy + * the active http agent + */ +helper.destroyProxyAgent = function destroyProxyAgent() { + require('../../lib/collector/http-agents').proxyAgent().destroy() +} diff --git a/test/lib/custom-assertions.js b/test/lib/custom-assertions.js new file mode 100644 index 0000000000..9d96084111 --- /dev/null +++ b/test/lib/custom-assertions.js @@ -0,0 +1,81 @@ +/* + * Copyright 2023 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' +const tap = require('tap') +tap.Test.prototype.addAssert('clmAttrs', 1, assertCLMAttrs) +tap.Test.prototype.addAssert('isNonWritable', 1, isNonWritable) +tap.Test.prototype.addAssert('compareSegments', 2, compareSegments) +tap.Test.prototype.addAssert('exactClmAttrs', 2, assertExactClmAttrs) + +function assertExactClmAttrs(segmentStub, expectedAttrs) { + const attrs = segmentStub.addAttribute.args + const attrsObj = attrs.reduce((obj, [key, value]) => { + obj[key] = value + return obj + }, {}) + this.same(attrsObj, expectedAttrs, 'CLM attrs should match') +} + +/** + * Asserts the appropriate Code Level Metrics attributes on a segment + * + * @param {object} params + * @param {object} params.segments list of segments to assert { segment, filepath, name } + * @param {boolean} params.enabled if CLM is enabled or not + */ +function assertCLMAttrs({ segments, enabled: clmEnabled }) { + segments.forEach((segment) => { + const attrs = segment.segment.getAttributes() + if (clmEnabled) { + this.equal(attrs['code.function'], segment.name, 'should have appropriate code.function') + this.ok( + attrs['code.filepath'].endsWith(segment.filepath), + 'should have appropriate code.filepath' + ) + this.match(attrs['code.lineno'], /[\d]+/, 'lineno should be a number') + this.match(attrs['code.column'], /[\d]+/, 'column should be a number') + } else { + this.notOk(attrs['code.function'], 'function should not exist') + this.notOk(attrs['code.filepath'], 'filepath should not exist') + this.notOk(attrs['code.lineno'], 'lineno should not exist') + this.notOk(attrs['code.column'], 'column should not exist') + } + }) +} + +/** + * assertion to test if a property is non-writable + * + * @param {Object} params + * @param {Object} params.obj obj to assign value + * @param {string} params.key key to assign value + * @param {string} params.value expected value of obj[key] + */ +function isNonWritable({ obj, key, value }) { + this.throws(function () { + obj[key] = 'testNonWritable test value' + }, new RegExp("(read only property '" + key + "'|Cannot set property " + key + ')')) + + if (value) { + this.equal(obj[key], value) + } else { + this.not(obj[key], 'testNonWritable test value', 'should not set value when non-writable') + } +} + +/** + * Verifies the expected length of children segments and that every + * id matches between a segment array and the children + * + * @param {Object} parent trace + * @param {Array} segments list of expected segments + */ +function compareSegments(parent, segments) { + this.ok(parent.children.length, segments.length, 'should be the same amount of children') + segments.forEach((segment, index) => { + this.equal(parent.children[index].id, segment.id, 'should have same ids') + }) +} diff --git a/test/lib/logging-helper.js b/test/lib/logging-helper.js index 3cd1142303..86c1a7973a 100644 --- a/test/lib/logging-helper.js +++ b/test/lib/logging-helper.js @@ -5,6 +5,9 @@ 'use strict' const helpers = module.exports +const tap = require('tap') + +tap.Test.prototype.addAssert('validateAnnotations', 2, validateLogLine) // NOTE: pino adds hostname to log lines which is why we don't check it here helpers.CONTEXT_KEYS = [ @@ -19,7 +22,7 @@ helpers.CONTEXT_KEYS = [ /** * To be registered as a tap assertion */ -helpers.validateLogLine = function validateLogLine({ line: logLine, message, level, config }) { +function validateLogLine({ line: logLine, message, level, config }) { this.equal( logLine['entity.name'], config.applications()[0], diff --git a/test/lib/metrics_helper.js b/test/lib/metrics_helper.js index a610b7634e..9d52790cb5 100644 --- a/test/lib/metrics_helper.js +++ b/test/lib/metrics_helper.js @@ -5,15 +5,15 @@ 'use strict' -const assert = require('chai').assert -const format = require('util').format +const tap = require('tap') const urltils = require('../../lib/util/urltils') +const { isSimpleObject } = require('../../lib/util/objects') -exports.assertMetrics = assertMetrics -exports.assertSegments = assertSegments exports.findSegment = findSegment exports.getMetricHostName = getMetricHostName -exports.tapAssertMetrics = tapAssertMetrics +tap.Test.prototype.addAssert('assertMetrics', 4, assertMetrics) +tap.Test.prototype.addAssert('assertSegments', 3, assertSegments) +tap.Test.prototype.addAssert('assertMetricValues', 3, assertMetricValues) /** * @param {Metrics} metrics metrics under test @@ -37,9 +37,9 @@ function assertMetrics(metrics, expected, exclusive, assertValues) { // Assertions about arguments because maybe something returned undefined // unexpectedly and is passed in, or a return type changed. This will // hopefully help catch that and make it obvious. - assert.isObject(metrics, 'first argument required to be an Metrics object') - assert.isArray(expected, 'second argument required to be an array of metrics') - assert.isBoolean(exclusive, 'third argument required to be a boolean if provided') + this.ok(isSimpleObject(metrics), 'first argument required to be an Metrics object') + this.ok(Array.isArray(expected), 'second argument required to be an array of metrics') + this.ok(typeof exclusive === 'boolean', 'third argument required to be a boolean if provided') if (assertValues === undefined) { assertValues = true @@ -48,39 +48,19 @@ function assertMetrics(metrics, expected, exclusive, assertValues) { for (let i = 0, len = expected.length; i < len; i++) { const expectedMetric = expected[i] const metric = metrics.getMetric(expectedMetric[0].name, expectedMetric[0].scope) - if (!metric) { - throw new Error(format('%j is missing from the metrics bucket', expectedMetric[0])) - } + this.ok(metric) if (assertValues) { - assert.deepEqual( - metric.toJSON(), - expectedMetric[1], - format( - '%j did not match (got %j, expected: %j)', - expectedMetric[0], - metric.toJSON(), - expectedMetric[1] - ) - ) + this.same(metric.toJSON(), expectedMetric[1]) } } if (exclusive) { const metricsList = metrics.toJSON() - assert.equal( - metricsList.length, - expected.length, - format( - 'exclusive set expected but there is a length mismatch (got: %j, expected %j)', - metricsList, - expected - ) - ) + this.equal(metricsList.length, expected.length) } } /** - * @param {Test} tap tap test object * @param {Transaction} transaction Nodejs agent transaction * @param {Array} expected Array of metric data where metric data is in this form: * [ @@ -97,7 +77,7 @@ function assertMetrics(metrics, expected, exclusive, assertValues) { * ] * @param {boolean} exact When true, found and expected metric lengths should match */ -function tapAssertMetrics(tap, transaction, expected, exact) { +function assertMetricValues(transaction, expected, exact) { const metrics = transaction.metrics for (let i = 0; i < expected.length; ++i) { @@ -114,14 +94,14 @@ function tapAssertMetrics(tap, transaction, expected, exact) { } const metric = metrics.getMetric(name, scope) - tap.ok(metric, 'should have expected metric name') + this.ok(metric, 'should have expected metric name') - tap.strictSame(metric.toJSON(), expectedMetric[1], 'metric values should match') + this.strictSame(metric.toJSON(), expectedMetric[1], 'metric values should match') } if (exact) { const metricsJSON = metrics.toJSON() - tap.equal(metricsJSON.length, expected.length, 'metrics length should match') + this.equal(metricsJSON.length, expected.length, 'metrics length should match') } } @@ -167,7 +147,7 @@ function assertSegments(parent, expected, options) { if (typeof sequenceItem === 'string') { child = children[childCount++] - assert.equal( + this.equal( child ? child.name : undefined, sequenceItem, 'segment "' + @@ -182,27 +162,18 @@ function assertSegments(parent, expected, options) { // child has no children if (!Array.isArray(expected[i + 1])) { // var children = child.children - assert( + this.ok( getChildren(child).length === 0, 'segment "' + child.name + '" should not have any children' ) } } else if (typeof sequenceItem === 'object') { - assertSegments(child, sequenceItem, options) + this.assertSegments(child, sequenceItem, options) } } // check if correct number of children was found - assert.equal( - children.length, - childCount, - format( - 'segment "%s" expected to have %j children, but got %j', - parent.name, - childCount, - children.length - ) - ) + this.equal(children.length, childCount) } else { for (let i = 0; i < expected.length; i++) { const sequenceItem = expected[i] @@ -214,9 +185,9 @@ function assertSegments(parent, expected, options) { child = parent.children[j] } } - assert.ok(child, 'segment "' + parent.name + '" should have child "' + sequenceItem + '"') + this.ok(child, 'segment "' + parent.name + '" should have child "' + sequenceItem + '"') if (typeof expected[i + 1] === 'object') { - assertSegments(child, expected[i + 1], exact) + this.assertSegments(child, expected[i + 1], exact) } } } diff --git a/test/unit/agent/intrinsics.test.js b/test/unit/agent/intrinsics.test.js index e7e76721f2..ba2289566a 100644 --- a/test/unit/agent/intrinsics.test.js +++ b/test/unit/agent/intrinsics.test.js @@ -7,8 +7,6 @@ const tap = require('tap') const helper = require('../../lib/agent_helper.js') -const chai = require('chai') -const assert = chai.assert const sinon = require('sinon') const Transaction = require('../../../lib/transaction') const crossAgentTests = require('../../lib/cross_agent_tests/cat/cat_map.json') @@ -40,25 +38,23 @@ tap.test('when CAT is disabled (default agent settings)', (t) => { const attrs = agent._addIntrinsicAttrsFromTransaction(trans) - chai - .expect(Object.keys(attrs)) - .to.have.members([ - 'duration', - 'name', - 'timestamp', - 'totalTime', - 'type', - 'webDuration', - 'error', - 'traceId', - 'guid', - 'priority', - 'sampled' - ]) - - chai.expect(attrs.duration).to.be.closeTo(expectedDuration, 0.001) - chai.expect(attrs.webDuration).to.be.closeTo(expectedDuration, 0.001) - chai.expect(attrs.totalTime).to.be.closeTo(expectedTotalTime, 0.001) + t.same(Object.keys(attrs), [ + 'webDuration', + 'timestamp', + 'name', + 'duration', + 'totalTime', + 'type', + 'error', + 'traceId', + 'guid', + 'priority', + 'sampled' + ]) + + t.equal(attrs.duration, expectedDuration) + t.equal(attrs.webDuration, expectedDuration) + t.equal(attrs.totalTime, expectedTotalTime) t.equal(attrs.timestamp, start) t.equal(attrs.name, test.transactionName) @@ -91,7 +87,7 @@ tap.test('when CAT is disabled (default agent settings)', (t) => { const transaction = new Transaction(agent) transaction.measure(NAMES.DB.ALL, null, 100) const attrs = agent._addIntrinsicAttrsFromTransaction(transaction) - assert.equal(attrs.databaseDuration, 0.1) + t.equal(attrs.databaseDuration, 0.1) t.end() }) @@ -176,17 +172,17 @@ tap.test('when CAT is enabled', (t) => { const attrs = agent._addIntrinsicAttrsFromTransaction(trans) const keys = [ - 'duration', - 'name', + 'webDuration', 'timestamp', - 'type', + 'name', + 'duration', 'totalTime', - 'webDuration', + 'type', 'error', 'nr.guid', + 'nr.tripId', 'nr.pathHash', 'nr.referringPathHash', - 'nr.tripId', 'nr.referringTransactionGuid', 'nr.alternatePathHashes', 'nr.apdexPerfZone' @@ -200,12 +196,12 @@ tap.test('when CAT is enabled', (t) => { keys.splice(keys.indexOf('nr.apdexPerfZone'), 1) } - chai.expect(Object.keys(attrs)).to.have.members(keys) - - chai.expect(attrs.duration).to.be.closeTo(expectedDuration, 0.001) - chai.expect(attrs.webDuration).to.be.closeTo(expectedDuration, 0.001) - chai.expect(attrs.totalTime).to.be.closeTo(expectedTotalTime, 0.001) + t.same(Object.keys(attrs), keys) + t.equal(attrs.duration, expectedDuration) + t.equal(attrs.webDuration, expectedDuration) + t.equal(attrs.totalTime, expectedTotalTime) + t.equal(attrs.duration, expectedDuration) t.equal(attrs.timestamp, start) t.equal(attrs.name, test.transactionName) t.equal(attrs.type, 'Transaction') diff --git a/test/unit/apdex.test.js b/test/unit/apdex.test.js index 871f5243ff..2fab81448f 100644 --- a/test/unit/apdex.test.js +++ b/test/unit/apdex.test.js @@ -4,51 +4,58 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const ApdexStats = require('../../lib/stats/apdex') +tap.Test.prototype.addAssert( + 'verifyApdexStats', + 2, + function verifyApdexStats(actualStats, expectedStats) { + this.equal(actualStats.satisfying, expectedStats.satisfying) + this.equal(actualStats.tolerating, expectedStats.tolerating) + this.equal(actualStats.frustrating, expectedStats.frustrating) + } +) -describe('ApdexStats', function () { - let statistics - - beforeEach(function () { - statistics = new ApdexStats(0.3) +tap.test('ApdexStats', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.statistics = new ApdexStats(0.3) }) - it('should throw when created with no tolerating value', function () { - /* eslint-disable no-unused-vars */ - let apdex = null - /* eslint-enable no-unused-vars */ - - expect(function () { - apdex = new ApdexStats() - }).throws('Apdex summary must be created with apdexT') + t.test('should throw when created with no tolerating value', function (t) { + t.throws(function () { + // eslint-disable-next-line no-new + new ApdexStats() + }, 'Apdex summary must be created with apdexT') + t.end() }) - it('should export apdexT in the 4th field of the timeslice', function () { - expect(statistics.toJSON()[3]).equal(0.3) + t.test('should export apdexT in the 4th field of the timeslice', function (t) { + const { statistics } = t.context + t.equal(statistics.toJSON()[3], 0.3) + t.end() }) - it('should export apdexT in the 5th field (why?) of the timeslice', function () { - expect(statistics.toJSON()[4]).equal(0.3) + t.test('should export apdexT in the 5th field (why?) of the timeslice', function (t) { + const { statistics } = t.context + t.equal(statistics.toJSON()[4], 0.3) + t.end() }) - it('should correctly summarize a sample set of statistics', function () { + t.test('should correctly summarize a sample set of statistics', function (t) { + const { statistics } = t.context statistics.recordValueInMillis(1251) statistics.recordValueInMillis(250) statistics.recordValueInMillis(487) const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } - verifyApdexStats(statistics, expectedStats) + t.verifyApdexStats(statistics, expectedStats) + t.end() }) - it('should correctly summarize another simple set of statistics', function () { + t.test('should correctly summarize another simple set of statistics', function (t) { + const { statistics } = t.context statistics.recordValueInMillis(120) statistics.recordValueInMillis(120) statistics.recordValueInMillis(120) @@ -56,16 +63,18 @@ describe('ApdexStats', function () { const expectedStats = { satisfying: 4, tolerating: 0, frustrating: 0 } - verifyApdexStats(statistics, expectedStats) + t.verifyApdexStats(statistics, expectedStats) + t.end() }) - it('should correctly merge summaries', function () { + t.test('should correctly merge summaries', function (t) { + const { statistics } = t.context statistics.recordValueInMillis(1251) statistics.recordValueInMillis(250) statistics.recordValueInMillis(487) const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } - verifyApdexStats(statistics, expectedStats) + t.verifyApdexStats(statistics, expectedStats) const other = new ApdexStats(0.3) other.recordValueInMillis(120) @@ -74,17 +83,12 @@ describe('ApdexStats', function () { other.recordValueInMillis(120) const expectedOtherStats = { satisfying: 4, tolerating: 0, frustrating: 0 } - verifyApdexStats(other, expectedOtherStats) + t.verifyApdexStats(other, expectedOtherStats) statistics.merge(other) const expectedMergedStats = { satisfying: 5, tolerating: 1, frustrating: 1 } - verifyApdexStats(statistics, expectedMergedStats) + t.verifyApdexStats(statistics, expectedMergedStats) + t.end() }) - - function verifyApdexStats(actualStats, expectedStats) { - expect(actualStats.satisfying).equal(expectedStats.satisfying) - expect(actualStats.tolerating).equal(expectedStats.tolerating) - expect(actualStats.frustrating).equal(expectedStats.frustrating) - } }) diff --git a/test/unit/api/api-start-background-transaction.test.js b/test/unit/api/api-start-background-transaction.test.js index f3ec3853e1..03a9f61647 100644 --- a/test/unit/api/api-start-background-transaction.test.js +++ b/test/unit/api/api-start-background-transaction.test.js @@ -8,7 +8,6 @@ const tap = require('tap') const API = require('../../../api') const helper = require('../../lib/agent_helper') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) tap.test('Agent API - startBackgroundTransaction', (t) => { t.autoend() diff --git a/test/unit/api/api-start-segment.test.js b/test/unit/api/api-start-segment.test.js index 4f83b5c03a..4d83164605 100644 --- a/test/unit/api/api-start-segment.test.js +++ b/test/unit/api/api-start-segment.test.js @@ -9,8 +9,6 @@ const tap = require('tap') const API = require('../../../api') const helper = require('../../lib/agent_helper') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) - tap.test('Agent API - startSegment', (t) => { t.autoend() diff --git a/test/unit/api/api-start-web-transaction.test.js b/test/unit/api/api-start-web-transaction.test.js index 093b39c446..03a77286ee 100644 --- a/test/unit/api/api-start-web-transaction.test.js +++ b/test/unit/api/api-start-web-transaction.test.js @@ -8,7 +8,6 @@ const tap = require('tap') const API = require('../../../api') const helper = require('../../lib/agent_helper') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) tap.test('Agent API - startWebTransaction', (t) => { t.autoend() diff --git a/test/unit/assert-metrics.test.js b/test/unit/assert-metrics.test.js deleted file mode 100644 index 9c928c3561..0000000000 --- a/test/unit/assert-metrics.test.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const assertMetrics = require('../lib/metrics_helper').assertMetrics -const assert = require('chai').assert -const Metrics = require('../../lib/metrics') -const MetricMapper = require('../../lib/metrics/mapper') -const MetricNormalizer = require('../../lib/metrics/normalizer') - -describe('metrics_helper.assertMetrics', function () { - let metrics - - beforeEach(function () { - metrics = createMetricsBucket() - }) - - it('should check the args', function () { - assert.throws( - function () { - assertMetrics() - }, - Error, - /first argument/, - 'missing first arg' - ) - - assert.throws( - function () { - assertMetrics([], [], true) - }, - Error, - /first argument/, - 'array first arg' - ) - - assert.throws( - function () { - assertMetrics('stuff', [], true) - }, - Error, - /first argument/, - 'string first arg' - ) - - assert.throws( - function () { - assertMetrics(metrics) - }, - Error, - /second argument/, - 'missing second arg' - ) - - assert.throws( - function () { - assertMetrics(metrics, {}, false) - }, - Error, - /second argument/, - 'object second arg' - ) - - assert.throws( - function () { - assertMetrics(metrics, 'string', true) - }, - Error, - /second argument/, - 'string second arg' - ) - - assert.throws( - function () { - assertMetrics(metrics, []) - }, - Error, - /third argument/, - 'missing third arg' - ) - - assert.throws( - function () { - assertMetrics(metrics, [], {}) - }, - Error, - /third argument/, - 'object third arg' - ) - - assert.throws( - function () { - assertMetrics(metrics, [], []) - }, - Error, - /third argument/, - 'array third arg' - ) - - assert.doesNotThrow(function () { - assertMetrics(metrics, [], true) - }, 'proper args') - }) - - it('should handle unscoped metrics', function () { - const myMetric = metrics.getOrCreateMetric('MyMetric') - myMetric.recordValue(1, 1) - const expected = [[{ name: 'MyMetric' }, [1, 1, 1, 1, 1, 1]]] - assertMetrics(metrics, expected, true) - }) - - it('should handle scoped metrics', function () { - const myMetric = metrics.getOrCreateMetric('MyMetric', 'SomeScope') - myMetric.recordValue(1, 1) - const expected = [[{ name: 'MyMetric', scope: 'SomeScope' }, [1, 1, 1, 1, 1, 1]]] - assertMetrics(metrics, expected, true) - }) -}) - -function createMetricsBucket() { - return new Metrics(1, new MetricMapper(), new MetricNormalizer({}, 'plain')) -} diff --git a/test/unit/collector/remote-method.test.js b/test/unit/collector/remote-method.test.js index 5995278891..ecb2a8d2ec 100644 --- a/test/unit/collector/remote-method.test.js +++ b/test/unit/collector/remote-method.test.js @@ -11,7 +11,7 @@ const url = require('url') const Config = require('../../../lib/config') const RemoteMethod = require('../../../lib/collector/remote-method') const helper = require('../../lib/agent_helper') -const { tapAssertMetrics } = require('../../lib/metrics_helper') +require('../../lib/metrics_helper') const NAMES = require('../../../lib/metrics/names') const BARE_AGENT = { config: {}, metrics: { measureBytes() {} } } @@ -812,8 +812,7 @@ tap.test('record data usage supportability metrics', (t) => { }) } - tapAssertMetrics( - t, + t.assertMetricValues( { metrics: agent.metrics }, @@ -860,8 +859,7 @@ tap.test('record data usage supportability metrics', (t) => { }) }) - tapAssertMetrics( - t, + t.assertMetricValues( { metrics: agent.metrics }, @@ -897,8 +895,7 @@ tap.test('record data usage supportability metrics', (t) => { method.invoke(payload, resolve) }) - tapAssertMetrics( - t, + t.assertMetricValues( { metrics: agent.metrics }, diff --git a/test/unit/collector/serverless.test.js b/test/unit/collector/serverless.test.js index a48fe1564e..d1d2e8209f 100644 --- a/test/unit/collector/serverless.test.js +++ b/test/unit/collector/serverless.test.js @@ -11,8 +11,6 @@ const os = require('os') const util = require('util') const zlib = require('zlib') const nock = require('nock') -const chai = require('chai') -const expect = chai.expect const sinon = require('sinon') const fs = require('fs') const fsOpenAsync = util.promisify(fs.open) @@ -281,7 +279,7 @@ tap.test('ServerlessCollector with output to custom pipe', (t) => { const buf = Buffer.from(writtenPayload[2], 'base64') zlib.gunzip(buf, (err, unpack) => { - expect(err).to.be.null + t.error(err) const payload = JSON.parse(unpack) t.ok(payload.data) t.ok(Object.keys(payload.data).length > 4000, `expected to be > 4000`) diff --git a/test/unit/custom-events/custom-event-aggregator.test.js b/test/unit/custom-events/custom-event-aggregator.test.js index 938375d179..21d38e76d2 100644 --- a/test/unit/custom-events/custom-event-aggregator.test.js +++ b/test/unit/custom-events/custom-event-aggregator.test.js @@ -4,12 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect +const tap = require('tap') const CustomEventAggregator = require('../../../lib/custom-events/custom-event-aggregator') const Metrics = require('../../../lib/metrics') @@ -17,10 +12,11 @@ const RUN_ID = 1337 const LIMIT = 5 const EXPECTED_METHOD = 'custom_event_data' -describe('Custom Event Aggregator', () => { +tap.test('Custom Event Aggregator', (t) => { + t.autoend() let eventAggregator - beforeEach(() => { + t.beforeEach(() => { eventAggregator = new CustomEventAggregator( { runId: RUN_ID, @@ -31,33 +27,36 @@ describe('Custom Event Aggregator', () => { ) }) - afterEach(() => { + t.afterEach(() => { eventAggregator = null }) - it('should set the correct default method', () => { + t.test('should set the correct default method', (t) => { const method = eventAggregator.method - expect(method).to.equal(EXPECTED_METHOD) + t.equal(method, EXPECTED_METHOD) + t.end() }) - it('toPayloadSync() should return json format of data', () => { + t.test('toPayloadSync() should return json format of data', (t) => { const rawEvent = [{ type: 'Custom' }, { foo: 'bar' }] eventAggregator.add(rawEvent) const payload = eventAggregator._toPayloadSync() - expect(payload.length).to.equal(2) + t.equal(payload.length, 2) const [runId, eventData] = payload - expect(runId).to.equal(RUN_ID) - expect(eventData).to.deep.equal([rawEvent]) + t.equal(runId, RUN_ID) + t.same(eventData, [rawEvent]) + t.end() }) - it('toPayloadSync() should return nothing with no event data', () => { + t.test('toPayloadSync() should return nothing with no event data', (t) => { const payload = eventAggregator._toPayloadSync() - expect(payload).to.not.exist + t.notOk(payload) + t.end() }) }) diff --git a/test/unit/db/trace.test.js b/test/unit/db/trace.test.js index 84ca2119a7..edfa8462f3 100644 --- a/test/unit/db/trace.test.js +++ b/test/unit/db/trace.test.js @@ -5,100 +5,95 @@ 'use strict' -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - +const tap = require('tap') const helper = require('../../lib/agent_helper') -const chai = require('chai') - -const expect = chai.expect - -describe('SQL trace', function () { - describe('attributes', function () { - let agent - beforeEach(function () { - agent = helper.loadMockedAgent({ - slow_sql: { - enabled: true - }, - transaction_tracer: { - record_sql: 'raw', - explain_threshold: 0 - } - }) +tap.test('SQL trace attributes', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.agent = helper.loadMockedAgent({ + slow_sql: { + enabled: true + }, + transaction_tracer: { + record_sql: 'raw', + explain_threshold: 0 + } }) + }) - afterEach(function () { - helper.unloadAgent(agent) - }) + t.afterEach(function (t) { + helper.unloadAgent(t.context.agent) + }) - it('should include all DT intrinsics sans parentId and parentSpanId', function (done) { - agent.config.distributed_tracing.enabled = true - agent.config.primary_application_id = 'test' - agent.config.account_id = 1 - agent.config.simple_compression = true - helper.runInTransaction(agent, function (tx) { - const payload = tx._createDistributedTracePayload().text() - tx.isDistributedTrace = null - tx._acceptDistributedTracePayload(payload) - agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') - agent.queries.prepareJSON((err, samples) => { - const sample = samples[0] - const attributes = sample[sample.length - 1] - expect(attributes.traceId).to.equal(tx.traceId) - expect(attributes.guid).to.equal(tx.id) - expect(attributes.priority).to.equal(tx.priority) - expect(attributes.sampled).to.equal(tx.sampled) - expect(attributes['parent.type']).to.equal('App') - expect(attributes['parent.app']).to.equal(agent.config.primary_application_id) - expect(attributes['parent.account']).to.equal(agent.config.account_id) - expect(attributes.parentId).to.be.undefined - expect(attributes.parentSpanId).to.be.undefined - done() - }) + t.test('should include all DT intrinsics sans parentId and parentSpanId', function (t) { + const { agent } = t.context + agent.config.distributed_tracing.enabled = true + agent.config.primary_application_id = 'test' + agent.config.account_id = 1 + agent.config.simple_compression = true + helper.runInTransaction(agent, function (tx) { + const payload = tx._createDistributedTracePayload().text() + tx.isDistributedTrace = null + tx._acceptDistributedTracePayload(payload) + agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') + agent.queries.prepareJSON((err, samples) => { + const sample = samples[0] + const attributes = sample[sample.length - 1] + t.equal(attributes.traceId, tx.traceId) + t.equal(attributes.guid, tx.id) + t.equal(attributes.priority, tx.priority) + t.equal(attributes.sampled, tx.sampled) + t.equal(attributes['parent.type'], 'App') + t.equal(attributes['parent.app'], agent.config.primary_application_id) + t.equal(attributes['parent.account'], agent.config.account_id) + t.notOk(attributes.parentId) + t.notOk(attributes.parentSpanId) + t.end() }) }) + }) - it('should serialize properly using prepareJSONSync', function () { - helper.runInTransaction(agent, function (tx) { - const query = 'select pg_sleep(1)' - agent.queries.add(tx.trace.root, 'postgres', query, 'FAKE STACK') - const sampleObj = agent.queries.samples.values().next().value - const sample = agent.queries.prepareJSONSync()[0] - expect(sample[0]).to.equal(tx.getFullName()) - expect(sample[1]).to.equal('') - expect(sample[2]).to.equal(sampleObj.trace.id) - expect(sample[3]).to.equal(query) - expect(sample[4]).to.equal(sampleObj.trace.metric) - expect(sample[5]).to.equal(sampleObj.callCount) - expect(sample[6]).to.equal(sampleObj.total) - expect(sample[7]).to.equal(sampleObj.min) - expect(sample[8]).to.equal(sampleObj.max) - }) + t.test('should serialize properly using prepareJSONSync', function (t) { + const { agent } = t.context + helper.runInTransaction(agent, function (tx) { + const query = 'select pg_sleep(1)' + agent.queries.add(tx.trace.root, 'postgres', query, 'FAKE STACK') + const sampleObj = agent.queries.samples.values().next().value + const sample = agent.queries.prepareJSONSync()[0] + t.equal(sample[0], tx.getFullName()) + t.equal(sample[1], '') + t.equal(sample[2], sampleObj.trace.id) + t.equal(sample[3], query) + t.equal(sample[4], sampleObj.trace.metric) + t.equal(sample[5], sampleObj.callCount) + t.equal(sample[6], sampleObj.total) + t.equal(sample[7], sampleObj.min) + t.equal(sample[8], sampleObj.max) + t.end() }) + }) - it('should include the proper priority on transaction end', function (done) { - agent.config.distributed_tracing.enabled = true - agent.config.primary_application_id = 'test' - agent.config.account_id = 1 - agent.config.simple_compression = true - helper.runInTransaction(agent, function (tx) { - agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') - agent.queries.prepareJSON((err, samples) => { - const sample = samples[0] - const attributes = sample[sample.length - 1] - expect(attributes.traceId).to.equal(tx.traceId) - expect(attributes.guid).to.equal(tx.id) - expect(attributes.priority).to.equal(tx.priority) - expect(attributes.sampled).to.equal(tx.sampled) - expect(attributes.parentId).to.be.undefined - expect(attributes.parentSpanId).to.be.undefined - expect(tx.sampled).to.equal(true) - expect(tx.priority).to.be.greaterThan(1) - done() - }) + t.test('should include the proper priority on transaction end', function (t) { + const { agent } = t.context + agent.config.distributed_tracing.enabled = true + agent.config.primary_application_id = 'test' + agent.config.account_id = 1 + agent.config.simple_compression = true + helper.runInTransaction(agent, function (tx) { + agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') + agent.queries.prepareJSON((err, samples) => { + const sample = samples[0] + const attributes = sample[sample.length - 1] + t.equal(attributes.traceId, tx.traceId) + t.equal(attributes.guid, tx.id) + t.equal(attributes.priority, tx.priority) + t.equal(attributes.sampled, tx.sampled) + t.notOk(attributes.parentId) + t.notOk(attributes.parentSpanId) + t.equal(tx.sampled, true) + t.ok(tx.priority > 1) + t.end() }) }) }) diff --git a/test/unit/db_util.test.js b/test/unit/db_util.test.js index e8934bfca5..f6a19b5152 100644 --- a/test/unit/db_util.test.js +++ b/test/unit/db_util.test.js @@ -4,51 +4,50 @@ */ 'use strict' +const tap = require('tap') +const util = require('../../lib/db/utils') -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() +tap.test('DB Utilities:', function (t) { + const useParser = util.extractDatabaseChangeFromUse -const chai = require('chai') -const expect = chai.expect -const util = require('../../lib/db/utils') + t.test('should match single statement use expressions', function (t) { + t.equal(useParser('use test_db;'), 'test_db') + t.equal(useParser('USE INIT'), 'INIT') + t.end() + }) + + t.test('should not be sensitive to ; omission', function (t) { + t.equal(useParser('use test_db'), 'test_db') + t.end() + }) + + t.test('should not be sensitive to extra ;', function (t) { + t.equal(useParser('use test_db;;;;;;'), 'test_db') + t.end() + }) + + t.test('should not be sensitive to extra white space', function (t) { + t.equal(useParser(' use test_db;'), 'test_db') + t.equal(useParser('use test_db;'), 'test_db') + t.equal(useParser(' use test_db;'), 'test_db') + t.equal(useParser('use test_db ;'), 'test_db') + t.equal(useParser('use test_db; '), 'test_db') + t.end() + }) + + t.test('should match backtick expressions', function (t) { + t.equal(useParser('use `test_db`;'), '`test_db`') + t.equal(useParser('use `☃☃☃☃☃☃`;'), '`☃☃☃☃☃☃`') + t.end() + }) -describe('DB Utilities:', function () { - describe('use statement parser', function () { - const useParser = util.extractDatabaseChangeFromUse - - it('should match single statement use expressions', function () { - expect(useParser('use test_db;')).equal('test_db') - expect(useParser('USE INIT')).equal('INIT') - }) - - it('should not be sensitive to ; omission', function () { - expect(useParser('use test_db')).equal('test_db') - }) - - it('should not be sensitive to extra ;', function () { - expect(useParser('use test_db;;;;;;')).equal('test_db') - }) - - it('should not be sensitive to extra white space', function () { - expect(useParser(' use test_db;')).equal('test_db') - expect(useParser('use test_db;')).equal('test_db') - expect(useParser(' use test_db;')).equal('test_db') - expect(useParser('use test_db ;')).equal('test_db') - expect(useParser('use test_db; ')).equal('test_db') - }) - - it('should match backtick expressions', function () { - expect(useParser('use `test_db`;')).equal('`test_db`') - expect(useParser('use `☃☃☃☃☃☃`;')).equal('`☃☃☃☃☃☃`') - }) - - it('should not match malformed use expressions', function () { - expect(useParser('use cxvozicjvzocixjv`oasidfjaosdfij`;')).equal(null) - expect(useParser('use `oasidfjaosdfij`123;')).equal(null) - expect(useParser('use `oasidfjaosdfij` 123;')).equal(null) - expect(useParser('use \u0001;')).equal(null) - expect(useParser('use oasidfjaosdfij 123;')).equal(null) - }) + t.test('should not match malformed use expressions', function (t) { + t.equal(useParser('use cxvozicjvzocixjv`oasidfjaosdfij`;'), null) + t.equal(useParser('use `oasidfjaosdfij`123;'), null) + t.equal(useParser('use `oasidfjaosdfij` 123;'), null) + t.equal(useParser('use \u0001;'), null) + t.equal(useParser('use oasidfjaosdfij 123;'), null) + t.end() }) + t.end() }) diff --git a/test/unit/distributed_tracing/dt-cats.test.js b/test/unit/distributed_tracing/dt-cats.test.js index 4ac7fa18b4..be409731c9 100644 --- a/test/unit/distributed_tracing/dt-cats.test.js +++ b/test/unit/distributed_tracing/dt-cats.test.js @@ -4,12 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect +const tap = require('tap') const Exception = require('../../../lib/errors').Exception const helper = require('../../lib/agent_helper') const recorder = require('../../../lib/metrics/recorders/distributed-trace') @@ -19,20 +14,21 @@ const recordSupportability = require('../../../lib/agent').prototype.recordSuppo const testCases = require('../../lib/cross_agent_tests/distributed_tracing/distributed_tracing.json') -describe('distributed tracing', function () { - let agent - - beforeEach(() => { - agent = helper.loadMockedAgent({ distributed_tracing: { enabled: true } }) +tap.test('distributed tracing', function (t) { + t.autoend() + t.beforeEach((t) => { + const agent = helper.loadMockedAgent({ distributed_tracing: { enabled: true } }) agent.recordSupportability = recordSupportability + t.context.agent = agent }) - afterEach(() => { - helper.unloadAgent(agent) + t.afterEach((t) => { + helper.unloadAgent(t.context.agent) }) testCases.forEach((testCase) => { - it(testCase.test_name, (done) => { + t.test(testCase.test_name, (t) => { + const { agent } = t.context agent.config.trusted_account_key = testCase.trusted_account_key agent.config.account_id = testCase.account_id agent.config.primary_application_id = 'test app' @@ -75,21 +71,21 @@ describe('distributed tracing', function () { Object.keys(exact).forEach((key) => { const match = keyRegex.exec(key) if (match) { - expect(created.d[match[1]]).to.equal(exact[key]) + t.equal(created.d[match[1]], exact[key]) } else { - expect(created.v).to.have.ordered.members(exact.v) + t.same(created.v, exact.v) } }) if (outbound.expected) { outbound.expected.forEach((key) => { - expect(created.d).to.have.property(keyRegex.exec(key)[1]) + t.ok(created.d.hasOwnProperty(keyRegex.exec(key)[1])) }) } if (outbound.unexpected) { outbound.unexpected.forEach((key) => { - expect(created.d).to.not.have.property(keyRegex.exec(key)[1]) + t.notOk(created.d.hasOwnProperty(keyRegex.exec(key)[1])) }) } }) @@ -99,7 +95,7 @@ describe('distributed tracing', function () { tx.end() const intrinsics = testCase.intrinsics intrinsics.target_events.forEach((type) => { - expect(type).to.be.oneOf(['Transaction', 'TransactionError', 'Span']) + t.ok(['Transaction', 'TransactionError', 'Span'].includes(type)) const common = intrinsics.common const specific = intrinsics[type] || {} @@ -120,7 +116,7 @@ describe('distributed tracing', function () { const arbitrary = (specific.expected || []).concat(common.expected || []) const unexpected = (specific.unexpected || []).concat(common.unexpected || []) - expect(toCheck).to.have.length.above(0) + t.ok(toCheck.length > 0) toCheck.forEach((event) => { // Span events are not payload-formatted straight out of the // aggregator. @@ -130,13 +126,13 @@ describe('distributed tracing', function () { const attributes = event[0] arbitrary.forEach((key) => { - expect(attributes, `${type} should have ${key}`).to.have.property(key) + t.ok(attributes[`${key}`], `${type} should have ${key}`) }) unexpected.forEach((key) => { - expect(attributes, `${type} should not have ${key}`).to.not.have.property(key) + t.notOk(attributes[`${key}`], `${type} should not have ${key}`) }) Object.keys(exact).forEach((key) => { - expect(attributes[key], `${type} should have equal ${key}`).to.equal(exact[key]) + t.equal(attributes[key], exact[key], `${type} should have equal ${key}`) }) }) }) @@ -146,11 +142,9 @@ describe('distributed tracing', function () { const metricName = metricPair[0] const callCount = metrics.getOrCreateMetric(metricName).callCount const metricCount = metricPair[1] - expect(callCount, `${metricName} should have ${metricCount} samples`).to.equal( - metricCount - ) + t.equal(callCount, metricCount, `${metricName} should have ${metricCount} samples`) }) - done() + t.end() }) }) }) diff --git a/test/unit/distributed_tracing/dt-payload.test.js b/test/unit/distributed_tracing/dt-payload.test.js index 69d19c272c..3334cb17f4 100644 --- a/test/unit/distributed_tracing/dt-payload.test.js +++ b/test/unit/distributed_tracing/dt-payload.test.js @@ -4,68 +4,56 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect - +const tap = require('tap') const DistributedTracePayload = require('../../../lib/transaction/dt-payload') const DistributedTracePayloadStub = DistributedTracePayload.Stub -describe('DistributedTracePayload', function () { - it('has a text method that returns the stringified payload', function () { +tap.test('DistributedTracePayload', function (t) { + t.test('has a text method that returns the stringified payload', function (t) { const payload = { a: 1, b: 'test' } const dt = new DistributedTracePayload(payload) const output = JSON.parse(dt.text()) - expect(output).to.have.property('v').that.is.an('array') - expect(output).to.have.property('d').that.is.an('object') - const keys = Object.keys(output.d) - expect(keys.length).to.equal(2) - for (let i = 0; i < keys.length; ++i) { - const key = keys[i] - expect(output.d[key]).to.equal(payload[key]) - } + t.ok(Array.isArray(output.v)) + t.same(output.d, payload) + t.end() }) - it('has a httpSafe method that returns the base64 encoded payload', function () { + t.test('has a httpSafe method that returns the base64 encoded payload', function (t) { const payload = { a: 1, b: 'test' } const dt = new DistributedTracePayload(payload) const output = JSON.parse(Buffer.from(dt.httpSafe(), 'base64').toString('utf-8')) - expect(output).to.have.property('v').that.is.an('array') - expect(output).to.have.property('d').that.is.an('object') - const keys = Object.keys(output.d) - expect(keys.length).to.equal(2) - for (let i = 0; i < keys.length; ++i) { - const key = keys[i] - expect(output.d[key]).to.equal(payload[key]) - } + t.ok(Array.isArray(output.v)) + t.same(output.d, payload) + t.end() }) + t.end() }) -describe('DistributedTracePayloadStub', function () { - it('has a httpSafe method that returns an empty string', function () { +tap.test('DistributedTracePayloadStub', function (t) { + t.test('has a httpSafe method that returns an empty string', function (t) { const payload = { a: 1, b: 'test' } const dt = new DistributedTracePayloadStub(payload) - expect(dt.httpSafe()).to.equal('') + t.equal(dt.httpSafe(), '') + t.end() }) - it('has a text method that returns an empty string', function () { + t.test('has a text method that returns an empty string', function (t) { const payload = { a: 1, b: 'test' } const dt = new DistributedTracePayloadStub(payload) - expect(dt.text()).to.equal('') + t.equal(dt.text(), '') + t.end() }) + t.end() }) diff --git a/test/unit/distributed_tracing/tracecontext.test.js b/test/unit/distributed_tracing/tracecontext.test.js index 7b0aa9620e..48c10768dd 100644 --- a/test/unit/distributed_tracing/tracecontext.test.js +++ b/test/unit/distributed_tracing/tracecontext.test.js @@ -4,26 +4,18 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../../lib/agent_helper') const Transaction = require('../../../lib/transaction') const TraceContext = require('../../../lib/transaction/tracecontext').TraceContext const sinon = require('sinon') -describe('TraceContext', function () { - let traceContext = null - let agent = null - let transaction = null +tap.test('TraceContext', function (t) { + t.autoend() const supportabilitySpy = sinon.spy() - beforeEach(function () { - agent = helper.loadMockedAgent({ + function beforeEach(t) { + const agent = helper.loadMockedAgent({ attributes: { enabled: true } }) @@ -34,62 +26,74 @@ describe('TraceContext', function () { agent.recordSupportability = supportabilitySpy - transaction = new Transaction(agent) - traceContext = new TraceContext(transaction) - }) + const transaction = new Transaction(agent) + t.context.traceContext = new TraceContext(transaction) + t.context.transaction = transaction + t.context.agent = agent + } - afterEach(function () { + function afterEach(t) { supportabilitySpy.resetHistory() - helper.unloadAgent(agent) - }) + helper.unloadAgent(t.context.agent) + } - describe('acceptTraceContextPayload', () => { - it('should accept valid trace context headers', () => { + t.test('acceptTraceContextPayload', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + + t.test('should accept valid trace context headers', (t) => { + const { traceContext } = t.context const traceparent = '00-00015f9f95352ad550284c27c5d3084c-00f067aa0ba902b7-00' // eslint-disable-next-line max-len const tracestate = `33@nr=0-0-33-2827902-7d3efb1b173fecfa-e8b91a159289ff74-1-1.23456-${Date.now()}` const tcd = traceContext.acceptTraceContextPayload(traceparent, tracestate) - expect(tcd.acceptedTraceparent).to.equal(true) - expect(tcd.acceptedTracestate).to.equal(true) - expect(tcd.traceId).to.equal('00015f9f95352ad550284c27c5d3084c') - expect(tcd.parentSpanId).to.equal('00f067aa0ba902b7') - expect(tcd.parentType).to.equal('App') - expect(tcd.accountId).to.equal('33') - expect(tcd.appId).to.equal('2827902') - expect(tcd.transactionId).to.equal('e8b91a159289ff74') - expect(tcd.sampled).to.equal(true) - expect(tcd.priority).to.equal(1.23456) - expect(tcd.transportDuration).to.be.below(10) - expect(tcd.transportDuration).to.be.at.least(0) - }) - - it('should not accept an empty traceparent header', () => { + t.equal(tcd.acceptedTraceparent, true) + t.equal(tcd.acceptedTracestate, true) + t.equal(tcd.traceId, '00015f9f95352ad550284c27c5d3084c') + t.equal(tcd.parentSpanId, '00f067aa0ba902b7') + t.equal(tcd.parentType, 'App') + t.equal(tcd.accountId, '33') + t.equal(tcd.appId, '2827902') + t.equal(tcd.transactionId, 'e8b91a159289ff74') + t.equal(tcd.sampled, true) + t.equal(tcd.priority, 1.23456) + t.ok(tcd.transportDuration < 10) + t.ok(tcd.transportDuration >= 0) + t.end() + }) + + t.test('should not accept an empty traceparent header', (t) => { + const { traceContext } = t.context const tcd = traceContext.acceptTraceContextPayload(null, '') - expect(tcd.acceptedTraceparent).to.equal(false) + t.equal(tcd.acceptedTraceparent, false) + t.end() }) - it('should not accept an invalid traceparent header', () => { + t.test('should not accept an invalid traceparent header', (t) => { + const { traceContext } = t.context const tcd = traceContext.acceptTraceContextPayload('invalid', '') - expect(tcd.acceptedTraceparent).to.equal(false) + t.equal(tcd.acceptedTraceparent, false) + t.end() }) - it('should not accept an invalid tracestate header', () => { + t.test('should not accept an invalid tracestate header', (t) => { + const { traceContext } = t.context const traceparent = '00-00015f9f95352ad550284c27c5d3084c-00f067aa0ba902b7-00' const tracestate = 'asdf,===asdf,,' const tcd = traceContext.acceptTraceContextPayload(traceparent, tracestate) - expect(supportabilitySpy.callCount).to.equal(2) - /* eslint-disable-next-line max-len */ - expect(supportabilitySpy.secondCall.args[0]).to.equal( - 'TraceContext/TraceState/Parse/Exception' - ) + t.equal(supportabilitySpy.callCount, 2) + t.equal(supportabilitySpy.secondCall.args[0], 'TraceContext/TraceState/Parse/Exception') - expect(tcd.acceptedTraceparent).to.equal(true) - expect(tcd.acceptedTracestate).to.equal(false) + t.equal(tcd.acceptedTraceparent, true) + t.equal(tcd.acceptedTracestate, false) + t.end() }) - it('should accept traceparent when tracestate missing', () => { + t.test('should accept traceparent when tracestate missing', (t) => { + const { agent } = t.context agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = false @@ -103,13 +107,15 @@ describe('TraceContext', function () { // The traceId should propagate const newTraceparent = txn.traceContext.createTraceparent() - expect(newTraceparent.startsWith('00-4bf92f3577b34da6a')).to.be.true + t.ok(newTraceparent.startsWith('00-4bf92f3577b34da6a')) txn.end() + t.end() }) }) - it('should accept traceparent when tracestate empty string', () => { + t.test('should accept traceparent when tracestate empty string', (t) => { + const { agent } = t.context agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = false @@ -124,185 +130,232 @@ describe('TraceContext', function () { // The traceId should propagate const newTraceparent = txn.traceContext.createTraceparent() - expect(newTraceparent.startsWith('00-4bf92f3577b34da6a')).to.be.true + t.ok(newTraceparent.startsWith('00-4bf92f3577b34da6a')) txn.end() + t.end() }) }) }) - describe('flags hex', function () { - it('should parse trace flags in the traceparent header', function () { + t.test('flags hex', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should parse trace flags in the traceparent header', function (t) { + const { traceContext } = t.context let flags = traceContext.parseFlagsHex('01') - expect(flags.sampled).to.be.true + t.ok(flags.sampled) flags = traceContext.parseFlagsHex('00') - expect(flags.sampled).to.be.false + t.notOk(flags.sampled) + t.end() }) - it('should return proper trace flags hex', function () { + t.test('should return proper trace flags hex', function (t) { + const { transaction, traceContext } = t.context transaction.sampled = false let flagsHex = traceContext.createFlagsHex() - expect(flagsHex).to.equal('00') + t.equal(flagsHex, '00') transaction.sampled = true flagsHex = traceContext.createFlagsHex() - expect(flagsHex).to.equal('01') + t.equal(flagsHex, '01') + t.end() }) }) - describe('_validateAndParseTraceParentHeader', () => { - it('should pass valid traceparent header', () => { + t.test('_validateAndParseTraceParentHeader', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should pass valid traceparent header', (t) => { + const { traceContext } = t.context const traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(traceparent).entryValid).to.be.ok + t.ok(traceContext._validateAndParseTraceParentHeader(traceparent).entryValid) + t.end() }) - it('should not pass 32 char string of all zeroes in traceid part of header', () => { + t.test('should not pass 32 char string of all zeroes in traceid part of header', (t) => { + const { traceContext } = t.context const allZeroes = '00-00000000000000000000000000000000-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(allZeroes).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(allZeroes).entryValid, false) + t.end() }) - it('should not pass 16 char string of all zeroes in parentid part of header', () => { + t.test('should not pass 16 char string of all zeroes in parentid part of header', (t) => { + const { traceContext } = t.context const allZeroes = '00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000000-00' - expect(traceContext._validateAndParseTraceParentHeader(allZeroes).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(allZeroes).entryValid, false) + t.end() }) - it('should not pass when traceid part contains uppercase letters', () => { + t.test('should not pass when traceid part contains uppercase letters', (t) => { + const { traceContext } = t.context const someCaps = '00-4BF92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(someCaps).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(someCaps).entryValid, false) + t.end() }) - it('should not pass when parentid part contains uppercase letters', () => { + t.test('should not pass when parentid part contains uppercase letters', (t) => { + const { traceContext } = t.context const someCaps = '00-4bf92f3577b34da6a3ce929d0e0e4736-00FFFFaa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(someCaps).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(someCaps).entryValid, false) + t.end() }) - it('should not pass when traceid part contains invalid chars', () => { + t.test('should not pass when traceid part contains invalid chars', (t) => { + const { traceContext } = t.context const invalidChar = '00-ZZf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(invalidChar).entryValid).to.equal( - false - ) + t.equal(traceContext._validateAndParseTraceParentHeader(invalidChar).entryValid, false) + t.end() }) - it('should not pass when parentid part contains invalid chars', () => { + t.test('should not pass when parentid part contains invalid chars', (t) => { + const { traceContext } = t.context const invalidChar = '00-aaf92f3577b34da6a3ce929d0e0e4736-00XX67aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(invalidChar).entryValid).to.equal( - false - ) + t.equal(traceContext._validateAndParseTraceParentHeader(invalidChar).entryValid, false) + t.end() }) - it('should not pass when tracid part is < 32 char long', () => { + t.test('should not pass when tracid part is < 32 char long', (t) => { + const { traceContext } = t.context const shorterStr = '00-4bf92f3-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid, false) + t.end() }) - it('should not pass when tracid part is > 32 char long', () => { + t.test('should not pass when tracid part is > 32 char long', (t) => { + const { traceContext } = t.context const longerStr = '00-4bf92f3577b34da6a3ce929d0e0e47366666666-00f067aa0ba902b7-00' - expect(traceContext._validateAndParseTraceParentHeader(longerStr).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(longerStr).entryValid, false) + t.end() }) - it('should not pass when parentid part is < 16 char long', () => { + t.test('should not pass when parentid part is < 16 char long', (t) => { + const { traceContext } = t.context const shorterStr = '00-aaf92f3577b34da6a3ce929d0e0e4736-ff-00' - expect(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid, false) + t.end() }) - it('should not pass when parentid part is > 16 char long', () => { + t.test('should not pass when parentid part is > 16 char long', (t) => { + const { traceContext } = t.context const shorterStr = '00-aaf92f3577b34da6a3ce929d0e0e4736-00XX67aa0ba902b72322332-00' - expect(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid).to.equal(false) + t.equal(traceContext._validateAndParseTraceParentHeader(shorterStr).entryValid, false) + t.end() }) }) - describe('_validateAndParseTraceStateHeader', () => { - it('should pass a valid tracestate header', () => { + t.test('_validateAndParseTraceStateHeader', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should pass a valid tracestate header', (t) => { + const { agent, traceContext } = t.context agent.config.trusted_account_key = '190' const goodTraceStateHeader = /* eslint-disable-next-line max-len */ '190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05-1-0.789-1563574856827,234234@foo=bar' const valid = traceContext._validateAndParseTraceStateHeader(goodTraceStateHeader) - expect(valid).to.be.ok - expect(valid.entryFound).to.be.true - expect(valid.entryValid).to.be.true - expect(valid.intrinsics.version).to.equal(0) - expect(valid.intrinsics.parentType).to.equal('App') - expect(valid.intrinsics.accountId).to.equal('709288') - expect(valid.intrinsics.appId).to.equal('8599547') - expect(valid.intrinsics.spanId).to.equal('f85f42fd82a4cf1d') - expect(valid.intrinsics.transactionId).to.equal('164d3b4b0d09cb05') - expect(valid.intrinsics.sampled).to.equal(true) - expect(valid.intrinsics.priority).to.equal(0.789) - expect(valid.intrinsics.timestamp).to.equal(1563574856827) - }) - - it('should fail mismatched trusted account ID in tracestate header', () => { + t.ok(valid) + t.equal(valid.entryFound, true) + t.equal(valid.entryValid, true) + t.equal(valid.intrinsics.version, 0) + t.equal(valid.intrinsics.parentType, 'App') + t.equal(valid.intrinsics.accountId, '709288') + t.equal(valid.intrinsics.appId, '8599547') + t.equal(valid.intrinsics.spanId, 'f85f42fd82a4cf1d') + t.equal(valid.intrinsics.transactionId, '164d3b4b0d09cb05') + t.equal(valid.intrinsics.sampled, true) + t.equal(valid.intrinsics.priority, 0.789) + t.equal(valid.intrinsics.timestamp, 1563574856827) + t.end() + }) + + t.test('should fail mismatched trusted account ID in tracestate header', (t) => { + const { agent, traceContext } = t.context agent.config.trusted_account_key = '666' const badTraceStateHeader = /* eslint-disable-next-line max-len */ '190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05-1-0.789-1563574856827,234234@foo=bar' const valid = traceContext._validateAndParseTraceStateHeader(badTraceStateHeader) - expect(supportabilitySpy.callCount).to.equal(1) - expect(supportabilitySpy.firstCall.args[0]).to.equal('TraceContext/TraceState/NoNrEntry') - expect(valid.entryFound).to.be.false - expect(valid.entryValid).to.be.undefined + t.equal(supportabilitySpy.callCount, 1) + t.equal(supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/NoNrEntry') + t.equal(valid.entryFound, false) + t.notOk(valid.entryValid) + t.end() }) - it('should generate supportability metric when vendor list parsing fails', () => { + t.test('should generate supportability metric when vendor list parsing fails', (t) => { + const { agent, traceContext } = t.context agent.config.trusted_account_key = '190' const badTraceStateHeader = /* eslint-disable-next-line max-len */ '190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05-1-0.789-1563574856827,234234@foobar' const valid = traceContext._validateAndParseTraceStateHeader(badTraceStateHeader) - expect(supportabilitySpy.callCount).to.equal(1) - expect(supportabilitySpy.firstCall.args[0]).to.equal( + t.equal(supportabilitySpy.callCount, 1) + t.equal( + supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/Parse/Exception/ListMember' ) - expect(valid.traceStateValid).to.be.false + t.equal(valid.traceStateValid, false) + t.end() }) - it('should fail mismatched trusted account ID in tracestate header', () => { + t.test('should fail mismatched trusted account ID in tracestate header', (t) => { + const { agent, traceContext } = t.context agent.config.trusted_account_key = '190' const badTimestamp = /* eslint-disable-next-line max-len */ '190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05-1-0.789-,234234@foo=bar' const valid = traceContext._validateAndParseTraceStateHeader(badTimestamp) - expect(valid.entryFound).to.be.true - expect(valid.entryValid).to.be.false + t.equal(valid.entryFound, true) + t.equal(valid.entryValid, false) + t.end() }) - it('should handle empty priority and sampled fields (mobile payload)', () => { + t.test('should handle empty priority and sampled fields (mobile payload)', (t) => { + const { agent, traceContext } = t.context agent.config.trusted_account_key = '190' const goodTraceStateHeader = /* eslint-disable-next-line max-len */ '190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05---1563574856827,234234@foo=bar' const valid = traceContext._validateAndParseTraceStateHeader(goodTraceStateHeader) - expect(valid).to.be.ok - expect(valid.entryFound).to.be.true - expect(valid.entryValid).to.be.true - expect(valid.intrinsics.version).to.equal(0) - expect(valid.intrinsics.parentType).to.equal('App') - expect(valid.intrinsics.accountId).to.equal('709288') - expect(valid.intrinsics.appId).to.equal('8599547') - expect(valid.intrinsics.spanId).to.equal('f85f42fd82a4cf1d') - expect(valid.intrinsics.transactionId).to.equal('164d3b4b0d09cb05') - expect(valid.intrinsics.sampled).to.not.exist - expect(valid.intrinsics.priority).to.not.exist - expect(valid.intrinsics.timestamp).to.equal(1563574856827) + t.ok(valid) + t.equal(valid.entryFound, true) + t.equal(valid.entryValid, true) + t.equal(valid.intrinsics.version, 0) + t.equal(valid.intrinsics.parentType, 'App') + t.equal(valid.intrinsics.accountId, '709288') + t.equal(valid.intrinsics.appId, '8599547') + t.equal(valid.intrinsics.spanId, 'f85f42fd82a4cf1d') + t.equal(valid.intrinsics.transactionId, '164d3b4b0d09cb05') + t.not(valid.intrinsics.sampled) + t.not(valid.intrinsics.priority) + t.equal(valid.intrinsics.timestamp, 1563574856827) + t.end() }) }) - describe('header creation', () => { - it('creating traceparent twice should give the same value', function () { + t.test('header creation', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('creating traceparent twice should give the same value', function (t) { + const { agent } = t.context helper.runInTransaction(agent, function (txn) { const childSegment = txn.trace.add('child') childSegment.start() @@ -310,12 +363,14 @@ describe('TraceContext', function () { const tp1 = txn.traceContext.createTraceparent() const tp2 = txn.traceContext.createTraceparent() - expect(tp1).to.equal(tp2) + t.equal(tp1, tp2) txn.end() + t.end() }) }) - it('should create valid headers', () => { + t.test('should create valid headers', (t) => { + const { agent } = t.context const trustedKey = '19000' const accountId = '190' const appId = '109354' @@ -329,18 +384,20 @@ describe('TraceContext', function () { childSegment.start() const headers = getTraceContextHeaders(txn) - expect(txn.traceContext._validateAndParseTraceParentHeader(headers.traceparent)).to.be.ok - expect(txn.traceContext._validateAndParseTraceStateHeader(headers.tracestate)).to.be.ok - expect(headers.tracestate.split('=')[0]).to.equal(`${trustedKey}@nr`) - expect(headers.tracestate.split('-')[6]).to.equal('0') - expect(headers.tracestate.split('-')[3]).to.equal(appId) - expect(headers.tracestate.split('-')[2]).to.equal(accountId) + t.ok(txn.traceContext._validateAndParseTraceParentHeader(headers.traceparent)) + t.ok(txn.traceContext._validateAndParseTraceStateHeader(headers.tracestate)) + t.equal(headers.tracestate.split('=')[0], `${trustedKey}@nr`) + t.equal(headers.tracestate.split('-')[6], '0') + t.equal(headers.tracestate.split('-')[3], appId) + t.equal(headers.tracestate.split('-')[2], accountId) txn.end() + t.end() }) }) - it('should accept first valid nr entry when duplicate entries exist', () => { + t.test('should accept first valid nr entry when duplicate entries exist', (t) => { + const { agent } = t.context const acctKey = '190' agent.config.trusted_account_key = acctKey const duplicateAcctTraceState = @@ -362,21 +419,22 @@ describe('TraceContext', function () { const valid = txn.traceContext._validateAndParseTraceStateHeader(duplicateAcctTraceState) const traceContextPayload = getTraceContextHeaders(txn) - expect(valid.entryFound).to.be.true - expect(valid.entryValid).to.be.true - expect(valid.vendors.match(`${acctKey}@nr`)).to.not.exist - + t.equal(valid.entryFound, true) + t.equal(valid.entryValid, true) + t.notOk(valid.vendors.includes(`${acctKey}@nr`)) const nrMatch = traceContextPayload.tracestate.match(/190@nr/g) || [] - expect(nrMatch.length, 'has only one nr entry').to.equal(1) + t.equal(nrMatch.length, 1, 'has only one nr entry') const nonNrMatch = traceContextPayload.tracestate.match(/42@bar/g) || [] - expect(nonNrMatch.length, 'contains non-nr entry').to.equal(1) + t.equal(nonNrMatch.length, 1, 'contains non-nr entry') txn.end() + t.end() }) }) - it('should not accept first nr entry when duplicate entries exist and its invalid', () => { + t.test('should not accept first nr entry when duplicate entries exist and its invalid', (t) => { + const { agent, traceContext } = t.context const acctKey = '190' agent.config.trusted_account_key = acctKey const duplicateAcctTraceState = @@ -384,12 +442,14 @@ describe('TraceContext', function () { '190@nr=bar,42@bar=foo,190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05-1-0.789-1563574856827' const valid = traceContext._validateAndParseTraceStateHeader(duplicateAcctTraceState) - expect(valid.entryFound).to.be.true - expect(valid.entryValid).to.be.false - expect(valid.vendors.match(`${acctKey}@nr`)).to.not.exist + t.equal(valid.entryFound, true) + t.equal(valid.entryValid, false) + t.notOk(valid.vendors.includes(`${acctKey}@nr`)) + t.end() }) - it('should propogate headers', () => { + t.test('should propagate headers', (t) => { + const { agent } = t.context agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = false @@ -406,16 +466,18 @@ describe('TraceContext', function () { // The parentId (current span id) of traceparent will change, but the traceId // should propagate - expect(headers.traceparent.startsWith('00-4bf92f3577b34da6a')).to.be.true + t.ok(headers.traceparent.startsWith('00-4bf92f3577b34da6a')) // The test key/value should propagate at the end of the string - expect(headers.tracestate.endsWith(tracestate)).to.be.true + t.ok(headers.tracestate.endsWith(tracestate)) txn.end() + t.end() }) }) - it('should generate parentId if no span/segment in context', (done) => { + t.test('should generate parentId if no span/segment in context', (t) => { + const { agent } = t.context // This is a corner case and ideally never happens but is potentially possible // due to state loss. @@ -437,20 +499,21 @@ describe('TraceContext', function () { const splitData = headers.traceparent.split('-') const [version, traceId, parentId] = splitData - expect(version).to.equal(expectedVersion) - expect(traceId).to.equal(expectedTraceId) + t.equal(version, expectedVersion) + t.equal(traceId, expectedTraceId) - expect(parentId).to.exist // we should generate *something* - expect(parentId.length).to.equal(16) // and it should be 16 chars + t.ok(parentId) // we should generate *something* + t.equal(parentId.length, 16) // and it should be 16 chars txn.end() - done() + t.end() }) }) }) - it('should not generate spanId if no span/segment in context', (done) => { + t.test('should not generate spanId if no span/segment in context', (t) => { + const { agent } = t.context // This is a corner case and ideally never happens but is potentially possible // due to state loss. @@ -471,7 +534,7 @@ describe('TraceContext', function () { const tracestate = outboundHeaders.tracestate // The test key/value should propagate at the end of the string - expect(tracestate.endsWith(incomingTraceState)).to.be.true + t.ok(tracestate.endsWith(incomingTraceState)) const secondListMemberIndex = tracestate.indexOf(incomingTraceState) const nrItem = tracestate.substring(0, secondListMemberIndex) @@ -479,16 +542,17 @@ describe('TraceContext', function () { const splitData = nrItem.split('-') const { 4: spanId } = splitData - expect(spanId).to.equal('') + t.equal(spanId, '') txn.end() - done() + t.end() }) }) }) - it('should generate new trace when receiving invalid traceparent', (done) => { + t.test('should generate new trace when receiving invalid traceparent', (t) => { + const { agent } = t.context agent.config.account_id = 'AccountId1' agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = true @@ -505,17 +569,18 @@ describe('TraceContext', function () { const splitData = headers.traceparent.split('-') const [version, traceId] = splitData - expect(version).to.equal('00') - expect(traceId).to.exist - expect(traceId).to.not.equal(unexpectedTraceId) + t.equal(version, '00') + t.ok(traceId) + t.not(traceId, unexpectedTraceId) txn.end() - done() + t.end() }) }) - it('should continue trace when receiving future traceparent version', (done) => { + t.test('should continue trace when receiving future traceparent version', (t) => { + const { agent } = t.context agent.config.account_id = 'AccountId1' agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = true @@ -532,16 +597,17 @@ describe('TraceContext', function () { const splitData = headers.traceparent.split('-') const [version, traceId] = splitData - expect(version).to.equal('00') - expect(traceId).to.equal(expectedTraceId) + t.equal(version, '00') + t.equal(traceId, expectedTraceId) txn.end() - done() + t.end() }) }) - it('should not allow extra fields for 00 traceparent version', (done) => { + t.test('should not allow extra fields for 00 traceparent version', (t) => { + const { agent } = t.context agent.config.account_id = 'AccountId1' agent.config.distributed_tracing.enabled = true agent.config.span_events.enabled = true @@ -558,16 +624,17 @@ describe('TraceContext', function () { const splitData = headers.traceparent.split('-') const [version, traceId] = splitData - expect(version).to.equal('00') - expect(traceId).to.not.equal(unexpectedTraceId) + t.equal(version, '00') + t.not(traceId, unexpectedTraceId) txn.end() - done() + t.end() }) }) - it('should handle combined headers with empty values', (done) => { + t.test('should handle combined headers with empty values', (t) => { + const { agent } = t.context // The http module will automatically combine headers // In the case of combining ['tracestate', ''] and ['tracestate', 'foo=1'] // An incoming header may look like tracestate: 'foo=1, '. @@ -587,59 +654,62 @@ describe('TraceContext', function () { const splitData = headers.traceparent.split('-') const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) const tracestate = headers.tracestate const listMembers = tracestate.split(',') const [, fooMember] = listMembers - expect(fooMember).to.equal('foo=1') + t.equal(fooMember, 'foo=1') txn.end() - done() + t.end() }) }) - it('should propogate existing list members when cannot accept newrelic list members', (done) => { - // missing trust key means can't accept/match newrelic header - agent.config.trusted_account_key = null - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = false + t.test( + 'should propogate existing list members when cannot accept newrelic list members', + (t) => { + const { agent } = t.context + // missing trust key means can't accept/match newrelic header + agent.config.trusted_account_key = null + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = false - const incomingTraceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00' - const incomingTracestate = - '33@nr=0-0-33-2827902-7d3efb1b173fecfa-e8b91a159289ff74-1-1.23456-1518469636035,test=test' + const incomingTraceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00' + const incomingTracestate = + '33@nr=0-0-33-2827902-7d3efb1b173fecfa-e8b91a159289ff74-1-1.23456-1518469636035,test=test' - helper.runInTransaction(agent, function (txn) { - const childSegment = txn.trace.add('child') - childSegment.start() + helper.runInTransaction(agent, function (txn) { + const childSegment = txn.trace.add('child') + childSegment.start() - txn.acceptTraceContextPayload(incomingTraceparent, incomingTracestate) + txn.acceptTraceContextPayload(incomingTraceparent, incomingTracestate) - expect(supportabilitySpy.callCount).to.equal(1) + t.equal(supportabilitySpy.callCount, 1) - // eslint-disable-next-line max-len - expect(supportabilitySpy.firstCall.args[0]).to.equal( - 'TraceContext/TraceState/Accept/Exception' - ) + // eslint-disable-next-line max-len + t.equal(supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/Accept/Exception') - const headers = getTraceContextHeaders(txn) - // The parentId (current span id) of traceparent will change, but the traceId - // should propagate - expect(headers.traceparent.startsWith('00-4bf92f3577b34da6a')).to.be.true + const headers = getTraceContextHeaders(txn) + // The parentId (current span id) of traceparent will change, but the traceId + // should propagate + t.ok(headers.traceparent.startsWith('00-4bf92f3577b34da6a')) - // The original tracestate should be propogated - expect(headers.tracestate).to.equal(incomingTracestate) + // The original tracestate should be propogated + t.equal(headers.tracestate, incomingTracestate) - txn.end() + txn.end() - done() - }) - }) + t.end() + }) + } + ) - it('should propogate existing when cannot accept or generate newrelic list member', (done) => { + t.test('should propogate existing when cannot accept or generate newrelic list member', (t) => { + const { agent } = t.context agent.config.trusted_account_key = null agent.config.account_id = null agent.config.distributed_tracing.enabled = true @@ -658,282 +728,285 @@ describe('TraceContext', function () { const headers = getTraceContextHeaders(txn) // The parentId (current span id) of traceparent will change, but the traceId // should propagate - expect(headers.traceparent.startsWith('00-4bf92f3577b34da6a')).to.be.true + t.ok(headers.traceparent.startsWith('00-4bf92f3577b34da6a')) // The original tracestate should be propogated - expect(headers.tracestate).to.equal(incomingTracestate) + t.equal(headers.tracestate, incomingTracestate) txn.end() - done() + t.end() }) }) - describe('traceparent parsing should accept and remove optional white space (OWS)', () => { - it('should handle leading white space', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle leading white space', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = ` 00-${expectedTraceId}-1234567890123456-01` - const incomingTraceState = 'test=test' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = ` 00-${expectedTraceId}-1234567890123456-01` + const incomingTraceState = 'test=test' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should handle leading tab', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle leading tab', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `\t00-${expectedTraceId}-1234567890123456-01` - const incomingTraceState = 'test=test' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `\t00-${expectedTraceId}-1234567890123456-01` + const incomingTraceState = 'test=test' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should handle trailing white space', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle trailing white space', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01 ` - const incomingTraceState = 'test=test' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01 ` + const incomingTraceState = 'test=test' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - txn.end() + txn.end() - done() - }) + t.end() }) }) - describe('tracestate parsing should accept and remove optional white space (OWS)', () => { - it('should handle white space and tabs for a single item', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle white space and tabs for a single item', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01` - const incomingTraceState = '\t foo=1 \t' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01` + const incomingTraceState = '\t foo=1 \t' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - const tracestate = headers.tracestate - const listMembers = tracestate.split(',') + const tracestate = headers.tracestate + const listMembers = tracestate.split(',') - const [, fooMember] = listMembers - expect(fooMember).to.equal('foo=1') + const [, fooMember] = listMembers + t.equal(fooMember, 'foo=1') - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should handle white space and tabs between list members', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle white space and tabs between list members', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01` - const incomingTraceState = 'foo=1 \t , \t bar=2, \t baz=3' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01` + const incomingTraceState = 'foo=1 \t , \t bar=2, \t baz=3' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - const tracestate = headers.tracestate - const listMembers = tracestate.split(',') + const tracestate = headers.tracestate + const listMembers = tracestate.split(',') - const [, fooMember, barMember, bazMember] = listMembers + const [, fooMember, barMember, bazMember] = listMembers - expect(fooMember).to.equal('foo=1') - expect(barMember).to.equal('bar=2') - expect(bazMember).to.equal('baz=3') + t.equal(fooMember, 'foo=1') + t.equal(barMember, 'bar=2') + t.equal(bazMember, 'baz=3') - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should handle trailing tab', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle trailing tab', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01\t` - const incomingTraceState = 'test=test' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `00-${expectedTraceId}-1234567890123456-01\t` + const incomingTraceState = 'test=test' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should handle leading and trailing white space and tabs', (done) => { - agent.config.account_id = 'AccountId1' - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should handle leading and trailing white space and tabs', (t) => { + const { agent } = t.context + agent.config.account_id = 'AccountId1' + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - const expectedTraceId = '12345678901234567890123456789012' - const futureTraceparent = `\t 00-${expectedTraceId}-1234567890123456-01 \t` - const incomingTraceState = 'test=test' + const expectedTraceId = '12345678901234567890123456789012' + const futureTraceparent = `\t 00-${expectedTraceId}-1234567890123456-01 \t` + const incomingTraceState = 'test=test' - helper.runInTransaction(agent, function (txn) { - txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) + helper.runInTransaction(agent, function (txn) { + txn.acceptTraceContextPayload(futureTraceparent, incomingTraceState) - const headers = getTraceContextHeaders(txn) - const splitData = headers.traceparent.split('-') - const [, traceId] = splitData + const headers = getTraceContextHeaders(txn) + const splitData = headers.traceparent.split('-') + const [, traceId] = splitData - expect(traceId).to.equal(expectedTraceId) + t.equal(traceId, expectedTraceId) - txn.end() + txn.end() - done() - }) + t.end() }) }) + }) - describe('should gracefully handle missing required tracestate fields', () => { - // During startup, there is a period of time where we may notice outbound - // requests (or via API call) and attempt to create traces before receiving - // required fields from server. + t.test('should gracefully handle missing required tracestate fields', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + // During startup, there is a period of time where we may notice outbound + // requests (or via API call) and attempt to create traces before receiving + // required fields from server. - it('should not create tracestate when accountId is missing', (done) => { - agent.config.account_id = null - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should not create tracestate when accountId is missing', (t) => { + const { agent } = t.context + agent.config.account_id = null + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - helper.runInTransaction(agent, function (txn) { - const headers = {} - txn.traceContext.addTraceContextHeaders(headers) + helper.runInTransaction(agent, function (txn) { + const headers = {} + txn.traceContext.addTraceContextHeaders(headers) - expect(headers).to.have.property('traceparent') - expect(headers).to.not.have.property('tracestate') + t.ok(headers.traceparent) + t.notOk(headers.tracestate) - expect(supportabilitySpy.callCount).to.equal(2) - // eslint-disable-next-line max-len - expect(supportabilitySpy.firstCall.args[0]).to.equal( - 'TraceContext/TraceState/Create/Exception' - ) + t.equal(supportabilitySpy.callCount, 2) + // eslint-disable-next-line max-len + t.equal(supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/Create/Exception') - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should not create tracestate when primary_application_id missing', (done) => { - agent.config.account_id = '12345' - agent.config.primary_application_id = null - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should not create tracestate when primary_application_id missing', (t) => { + const { agent } = t.context + agent.config.account_id = '12345' + agent.config.primary_application_id = null + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - helper.runInTransaction(agent, function (txn) { - const headers = {} - txn.traceContext.addTraceContextHeaders(headers) + helper.runInTransaction(agent, function (txn) { + const headers = {} + txn.traceContext.addTraceContextHeaders(headers) - expect(headers).to.have.property('traceparent') - expect(headers).to.not.have.property('tracestate') + t.ok(headers.traceparent) + t.notOk(headers.tracestate) - expect(supportabilitySpy.callCount).to.equal(2) - // eslint-disable-next-line max-len - expect(supportabilitySpy.firstCall.args[0]).to.equal( - 'TraceContext/TraceState/Create/Exception' - ) + t.equal(supportabilitySpy.callCount, 2) + // eslint-disable-next-line max-len + t.equal(supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/Create/Exception') - txn.end() + txn.end() - done() - }) + t.end() }) + }) - it('should not create tracestate when trusted_account_key missing', (done) => { - agent.config.account_id = '12345' - agent.config.primary_application_id = 'appId' - agent.config.trusted_account_key = null - agent.config.distributed_tracing.enabled = true - agent.config.span_events.enabled = true + t.test('should not create tracestate when trusted_account_key missing', (t) => { + const { agent } = t.context + agent.config.account_id = '12345' + agent.config.primary_application_id = 'appId' + agent.config.trusted_account_key = null + agent.config.distributed_tracing.enabled = true + agent.config.span_events.enabled = true - helper.runInTransaction(agent, function (txn) { - const headers = {} - txn.traceContext.addTraceContextHeaders(headers) + helper.runInTransaction(agent, function (txn) { + const headers = {} + txn.traceContext.addTraceContextHeaders(headers) - expect(headers).to.have.property('traceparent') - expect(headers).to.not.have.property('tracestate') + t.ok(headers.traceparent) + t.notOk(headers.tracestate) - expect(supportabilitySpy.callCount).to.equal(2) - // eslint-disable-next-line max-len - expect(supportabilitySpy.firstCall.args[0]).to.equal( - 'TraceContext/TraceState/Create/Exception' - ) + t.equal(supportabilitySpy.callCount, 2) + // eslint-disable-next-line max-len + t.equal(supportabilitySpy.firstCall.args[0], 'TraceContext/TraceState/Create/Exception') - txn.end() + txn.end() - done() - }) + t.end() }) }) }) diff --git a/test/unit/header-attributes.test.js b/test/unit/header-attributes.test.js index 6aeb3c13cf..89432f0293 100644 --- a/test/unit/header-attributes.test.js +++ b/test/unit/header-attributes.test.js @@ -4,31 +4,45 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - +const tap = require('tap') const helper = require('../lib/agent_helper') -const expect = require('chai').expect const headerAttributes = require('../../lib/header-attributes') const DESTINATIONS = require('../../lib/config/attribute-filter').DESTINATIONS - -describe('header-attributes', () => { - let agent = null - - beforeEach(() => { - agent = helper.loadMockedAgent() - }) - - afterEach(() => { - helper.unloadAgent(agent) - agent = null - }) - - describe('#collectRequestHeaders', () => { - it('should be case insensitive when allow_all_headers is false', (done) => { +function beforeEach(t) { + const config = { + attributes: { + exclude: [ + 'request.headers.cookie', + 'request.headers.authorization', + 'request.headers.proxyAuthorization', + 'request.headers.setCookie*', + 'request.headers.x*', + 'response.headers.cookie', + 'response.headers.authorization', + 'response.headers.proxyAuthorization', + 'response.headers.setCookie*', + 'response.headers.x*' + ] + } + } + t.context.agent = helper.loadMockedAgent(config) +} + +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} + +tap.test('header-attributes', (t) => { + t.autoend() + + t.test('#collectRequestHeaders', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + + t.test('should be case insensitive when allow_all_headers is false', (t) => { + const { agent } = t.context agent.config.allow_all_headers = false const headers = { Accept: 'acceptValue' @@ -38,13 +52,14 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.have.property('request.headers.accept', 'acceptValue') - expect(attributes).to.not.have.property('Accept') + t.equal(attributes['request.headers.accept'], 'acceptValue') + t.notOk(attributes.Accept) agent.config.allow_all_headers = true - done() + t.end() }) }) - it('should strip `-` from headers', (done) => { + t.test('should strip `-` from headers', (t) => { + const { agent } = t.context const headers = { 'content-type': 'valid-type' } @@ -53,13 +68,14 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.have.property('request.headers.contentType', 'valid-type') - expect(attributes).to.not.have.property('content-type') - done() + t.equal(attributes['request.headers.contentType'], 'valid-type') + t.notOk(attributes['content-type']) + t.end() }) }) - it('should lowercase first letter in headers', (done) => { + t.test('should lowercase first letter in headers', (t) => { + const { agent } = t.context const headers = { 'Content-Type': 'valid-type' } @@ -68,14 +84,15 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.have.property('request.headers.contentType', 'valid-type') - expect(attributes).to.not.have.property('Content-Type') - expect(attributes).to.not.have.property('ContentType') - done() + t.equal(attributes['request.headers.contentType'], 'valid-type') + t.notOk(attributes['Content-Type']) + t.notOk(attributes.ContentType) + t.end() }) }) - it('should capture a scrubbed version of the referer header', (done) => { + t.test('should capture a scrubbed version of the referer header', (t) => { + const { agent } = t.context const refererUrl = 'https://www.google.com/search/cats?scrubbed=false' const headers = { @@ -87,17 +104,16 @@ describe('header-attributes', () => { const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.have.property( - 'request.headers.referer', - 'https://www.google.com/search/cats' - ) + t.equal(attributes['request.headers.referer'], 'https://www.google.com/search/cats') - done() + t.end() }) }) - describe('with allow_all_headers set to false', () => { - it('should only collect allowed agent-specified headers', (done) => { + t.test( + 'with allow_all_headers set to false should only collect allowed agent-specified headers', + (t) => { + const { agent } = t.context agent.config.allow_all_headers = false const headers = { @@ -110,17 +126,19 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.not.have.property('request.headers.invalid') - expect(attributes).to.have.property('request.headers.referer', 'valid-referer') - expect(attributes).to.have.property('request.headers.contentType', 'valid-type') + t.notOk(attributes['request.headers.invalid']) + t.equal(attributes['request.headers.referer'], 'valid-referer') + t.equal(attributes['request.headers.contentType'], 'valid-type') - done() + t.end() }) - }) - }) + } + ) - describe('with allow_all_headers set to false', () => { - it('should collect allowed headers as span attributes', (done) => { + t.test( + 'with allow_all_headers set to false should collect allowed headers as span attributes', + (t) => { + const { agent } = t.context agent.config.allow_all_headers = false const headers = { @@ -133,22 +151,24 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.not.have.property('request.headers.invalid') - expect(attributes).to.have.property('request.headers.referer', 'valid-referer') - expect(attributes).to.have.property('request.headers.contentType', 'valid-type') + t.notOk(attributes['request.headers.invalid']) + t.equal(attributes['request.headers.referer'], 'valid-referer') + t.equal(attributes['request.headers.contentType'], 'valid-type') const segment = transaction.agent.tracer.getSegment() const spanAttributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT) - expect(spanAttributes).to.have.property('request.headers.referer', 'valid-referer') - expect(spanAttributes).to.have.property('request.headers.contentType', 'valid-type') - done() + t.equal(spanAttributes['request.headers.referer'], 'valid-referer') + t.equal(spanAttributes['request.headers.contentType'], 'valid-type') + t.end() }) - }) - }) + } + ) - describe('with allow_all_headers set to true', () => { - it('should collect all headers not filtered by `exclude` rules', (done) => { + t.test( + 'with allow_all_headers set to true should collect all headers not filtered by `exclude` rules', + (t) => { + const { agent } = t.context agent.config.allow_all_headers = true const headers = { @@ -162,21 +182,26 @@ describe('header-attributes', () => { headerAttributes.collectRequestHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.not.have.property('request.headers.x-filtered-out') - expect(attributes).to.not.have.property('request.headers.xFilteredOut') - expect(attributes).to.not.have.property('request.headers.XFilteredOut') - expect(attributes).to.have.property('request.headers.valid', 'header') - expect(attributes).to.have.property('request.headers.referer', 'valid-referer') - expect(attributes).to.have.property('request.headers.contentType', 'valid-type') - done() + t.notOk(attributes['request.headers.x-filtered-out']) + t.notOk(attributes['request.headers.xFilteredOut']) + t.notOk(attributes['request.headers.XFilteredOut']) + t.equal(attributes['request.headers.valid'], 'header') + t.equal(attributes['request.headers.referer'], 'valid-referer') + t.equal(attributes['request.headers.contentType'], 'valid-type') + t.end() }) - }) - }) + } + ) }) - describe('#collectResponseHeaders', () => { - describe('with allow_all_headers set to false', () => { - it('should only collect allowed agent-specified headers', (done) => { + t.test('#collectResponseHeaders', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test( + 'with allow_all_headers set to false should only collect allowed agent-specified headers', + (t) => { + const { agent } = t.context agent.config.allow_all_headers = false const headers = { @@ -188,15 +213,17 @@ describe('header-attributes', () => { headerAttributes.collectResponseHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.not.have.property('response.headers.invalid') - expect(attributes).to.have.property('response.headers.contentType', 'valid-type') - done() + t.notOk(attributes['response.headers.invalid']) + t.equal(attributes['response.headers.contentType'], 'valid-type') + t.end() }) - }) - }) + } + ) - describe('with allow_all_headers set to true', () => { - it('should collect all headers not filtered by `exclude` rules', (done) => { + t.test( + 'with allow_all_headers set to true should collect all headers not filtered by `exclude` rules', + (t) => { + const { agent } = t.context agent.config.allow_all_headers = true const headers = { @@ -209,14 +236,14 @@ describe('header-attributes', () => { headerAttributes.collectResponseHeaders(headers, transaction) const attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE) - expect(attributes).to.not.have.property('response.headers.x-filtered-out') - expect(attributes).to.not.have.property('response.headers.xFilteredOut') - expect(attributes).to.not.have.property('response.headers.XFilteredOut') - expect(attributes).to.have.property('response.headers.valid', 'header') - expect(attributes).to.have.property('response.headers.contentType', 'valid-type') - done() + t.notOk(attributes['response.headers.x-filtered-out']) + t.notOk(attributes['response.headers.xFilteredOut']) + t.notOk(attributes['response.headers.XFilteredOut']) + t.equal(attributes['response.headers.valid'], 'header') + t.equal(attributes['response.headers.contentType'], 'valid-type') + t.end() }) - }) - }) + } + ) }) }) diff --git a/test/unit/header-processing.test.js b/test/unit/header-processing.test.js index bd4aa30ba5..e0dbe9275e 100644 --- a/test/unit/header-processing.test.js +++ b/test/unit/header-processing.test.js @@ -4,55 +4,41 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect - +const tap = require('tap') const headerProcessing = require('../../lib/header-processing') -describe('header-processing', () => { - describe('#getContentLengthFromHeaders', () => { - it('should return content-length headers, case insensitive', () => { +tap.test('header-processing', (t) => { + t.test('#getContentLengthFromHeaders', (t) => { + t.test('should return content-length headers, case insensitive', (t) => { // does it work? - expect(headerProcessing.getContentLengthFromHeaders({ 'Content-Length': 100 })).to.equal(100) + t.equal(headerProcessing.getContentLengthFromHeaders({ 'Content-Length': 100 }), 100) // does it work with weird casing? - expect(headerProcessing.getContentLengthFromHeaders({ 'ConTent-LenGth': 100 })).to.equal(100) + t.equal(headerProcessing.getContentLengthFromHeaders({ 'ConTent-LenGth': 100 }), 100) // does it ignore other headers? - expect( + t.equal( headerProcessing.getContentLengthFromHeaders({ 'zip': 'zap', 'Content-Length': 100, 'foo': 'bar' - }) - ).to.equal(100) - - // does it return _exactly_, type including, what's in the header - // this captures the exact behavior of the legacy code - expect( - headerProcessing.getContentLengthFromHeaders({ - 'zip': 'zap', - 'Content-Length': '100', - 'foo': 'bar' - }) - ).to.equal('100') + }), + 100 + ) // when presented with two headers that are the same name - // but different case, does it prefer the first one found. + // but different case, does t.test prefer the first one found. // This captures the exact behavior of the legacy code we're // replacing - expect( + t.equal( headerProcessing.getContentLengthFromHeaders({ 'zip': 'zap', 'content-length': 50, 'Content-Length': 100, 'foo': 'bar' - }) - ).to.equal(50) + }), + 50 + ) // doesn't fail when working with null prototype objects // (returned by res.getHeaders() is -- some? all? versions @@ -62,24 +48,27 @@ describe('header-processing', () => { fixture['content-length'] = 49 fixture['Content-Length'] = 100 fixture.foo = 'bar' - expect(headerProcessing.getContentLengthFromHeaders(fixture)).to.equal(49) + t.equal(headerProcessing.getContentLengthFromHeaders(fixture), 49) + t.end() }) - it('should return -1 if there is no header', () => { - expect(headerProcessing.getContentLengthFromHeaders({})).to.equal(-1) + t.test('should return -1 if there is no header', (t) => { + t.equal(headerProcessing.getContentLengthFromHeaders({}), -1) - expect(headerProcessing.getContentLengthFromHeaders('foo')).to.equal(-1) + t.equal(headerProcessing.getContentLengthFromHeaders('foo'), -1) - expect(headerProcessing.getContentLengthFromHeaders([])).to.equal(-1) + t.equal(headerProcessing.getContentLengthFromHeaders([]), -1) - expect(headerProcessing.getContentLengthFromHeaders({ foo: 'bar', zip: 'zap' })).to.equal(-1) + t.equal(headerProcessing.getContentLengthFromHeaders({ foo: 'bar', zip: 'zap' }), -1) + t.end() }) + t.end() }) - describe('#getQueueTime', () => { + t.test('#getQueueTime', (t) => { // This header can hold up to 4096 bytes which could quickly fill up logs. // Do not log a level higher than debug. - it('should not log invalid raw queue time higher than debug level', () => { + t.test('should not log invalid raw queue time higher than debug level', (t) => { const invalidRawQueueTime = 'z1232442z' const requestHeaders = { 'x-queue-start': invalidRawQueueTime @@ -98,9 +87,10 @@ describe('header-processing', () => { const queueTime = headerProcessing.getQueueTime(mockLogger, requestHeaders) - expect(queueTime).to.not.exist - expect(didLogHighLevel).to.be.false - expect(didLogLowLevel).to.be.true + t.not(queueTime) + t.equal(didLogHighLevel, false) + t.equal(didLogLowLevel, true) + t.end() function didLogRawQueueTime(args) { let didLog = false @@ -127,5 +117,7 @@ describe('header-processing', () => { } } }) + t.end() }) + t.end() }) diff --git a/test/unit/metric/datastore-instance.test.js b/test/unit/metric/datastore-instance.test.js index 4449501c16..e7f891eb23 100644 --- a/test/unit/metric/datastore-instance.test.js +++ b/test/unit/metric/datastore-instance.test.js @@ -5,32 +5,29 @@ 'use strict' -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() +const tap = require('tap') -const expect = require('chai').expect const helper = require('../../lib/agent_helper') const DatastoreShim = require('../../../lib/shim/datastore-shim') const ParsedStatement = require('../../../lib/db/parsed-statement') const tests = require('../../lib/cross_agent_tests/datastores/datastore_instances') -describe('Datastore instance metrics collected via the datastore shim', function () { - let agent = null - - beforeEach(function () { - agent = helper.loadMockedAgent() +tap.test('Datastore instance metrics collected via the datastore shim', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.agent = helper.loadMockedAgent() }) - afterEach(function () { + t.afterEach(function (t) { + const { agent } = t.context if (agent) { helper.unloadAgent(agent) } - agent = null }) tests.forEach(function (test) { - it(test.name, function (done) { + t.test(test.name, function (t) { + const { agent } = t.context agent.config.getHostnameSafe = function () { return test.system_hostname } @@ -68,29 +65,29 @@ describe('Datastore instance metrics collected via the datastore shim', function testInstrumented.query() tx.end() - expect(getMetrics(agent).unscoped).to.have.property(test.expected_instance_metric) - done() + t.ok(getMetrics(agent).unscoped[test.expected_instance_metric]) + t.end() }) }) }) }) -describe('Datastore instance metrics captured through the segment', function () { - let agent = null - - beforeEach(function () { - agent = helper.loadMockedAgent() +tap.test('Datastore instance metrics captured through the segment', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.agent = helper.loadMockedAgent() }) - afterEach(function () { + t.afterEach(function (t) { + const { agent } = t.context if (agent) { helper.unloadAgent(agent) } - agent = null }) tests.forEach(function (test) { - it(test.name, function (done) { + t.test(test.name, function (t) { + const { agent } = t.context agent.config.getHostnameSafe = function () { return test.system_hostname } @@ -125,8 +122,8 @@ describe('Datastore instance metrics captured through the segment', function () child.touch() tx.end() - expect(getMetrics(agent).unscoped).to.have.property(test.expected_instance_metric) - done() + t.ok(getMetrics(agent).unscoped[test.expected_instance_metric]) + t.end() }) }) }) diff --git a/test/unit/metric/metric-aggregator.test.js b/test/unit/metric/metric-aggregator.test.js index b569384086..c02e255bc2 100644 --- a/test/unit/metric/metric-aggregator.test.js +++ b/test/unit/metric/metric-aggregator.test.js @@ -4,12 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect +const tap = require('tap') const sinon = require('sinon') const MetricAggregator = require('../../../lib/metrics/metric-aggregator') const MetricMapper = require('../../../lib/metrics/mapper') @@ -20,449 +15,453 @@ const RUN_ID = 1337 const EXPECTED_METHOD = 'metric_data' const EXPECTED_APDEX_T = 0.1 const EXPECTED_START_SECONDS = 10 +const MEGABYTE = 1024 * 1024 -describe('Metric Aggregator', () => { - let metricAggregator - let fakeCollectorApi = null - let mapper = null - let normalizer = null - let testClock = null - - beforeEach(() => { - testClock = sinon.useFakeTimers({ now: EXPECTED_START_SECONDS * 1000 }) +tap.test('Metric Aggregator', (t) => { + t.beforeEach((t) => { + t.context.testClock = sinon.useFakeTimers({ now: EXPECTED_START_SECONDS * 1000 }) - fakeCollectorApi = {} + const fakeCollectorApi = {} fakeCollectorApi[EXPECTED_METHOD] = () => {} - mapper = new MetricMapper() - normalizer = new MetricNormalizer({}, 'metric name') + t.context.mapper = new MetricMapper() + t.context.normalizer = new MetricNormalizer({}, 'metric name') - metricAggregator = new MetricAggregator( + t.context.metricAggregator = new MetricAggregator( { runId: RUN_ID, apdexT: EXPECTED_APDEX_T, - mapper: mapper, - normalizer: normalizer + mapper: t.context.mapper, + normalizer: t.context.normalizer }, fakeCollectorApi ) }) - afterEach(() => { + t.afterEach((t) => { + const { testClock } = t.context testClock.restore() - testClock = null - - metricAggregator = null - fakeCollectorApi = null - mapper = null - normalizer = null }) - it('should set the correct default method', () => { + t.test('should set the correct default method', (t) => { + const { metricAggregator } = t.context const method = metricAggregator.method - expect(method).to.equal(EXPECTED_METHOD) + t.equal(method, EXPECTED_METHOD) + t.end() }) - describe('reconfigure()', () => { - it('should update runId', () => { - const expectedRunId = 'new run id' - const fakeConfig = { run_id: expectedRunId } + t.test('should update runId on reconfigure', (t) => { + const { metricAggregator } = t.context + const expectedRunId = 'new run id' + const fakeConfig = { run_id: expectedRunId } - metricAggregator.reconfigure(fakeConfig) + metricAggregator.reconfigure(fakeConfig) - expect(metricAggregator.runId).to.equal(expectedRunId) - }) + t.equal(metricAggregator.runId, expectedRunId) + t.end() + }) - it('should update apdexT', () => { - const expectedApdexT = 2000 - const fakeConfig = { - apdex_t: expectedApdexT - } + t.test('should update apdexT on reconfigure', (t) => { + const { metricAggregator } = t.context + const expectedApdexT = 2000 + const fakeConfig = { + apdex_t: expectedApdexT + } - metricAggregator.reconfigure(fakeConfig) + metricAggregator.reconfigure(fakeConfig) - expect(metricAggregator._apdexT).to.equal(expectedApdexT) - expect(metricAggregator._metrics.apdexT).to.equal(expectedApdexT) - }) + t.equal(metricAggregator._apdexT, expectedApdexT) + t.equal(metricAggregator._metrics.apdexT, expectedApdexT) + t.end() }) - describe('empty', () => { - it('should be true when no metrics added', () => { - expect(metricAggregator.empty).to.be.true - }) - - it('should be false when metrics added', () => { - metricAggregator.getOrCreateMetric('myMetric') - expect(metricAggregator.empty).to.be.false - }) + t.test('should be true when no metrics added', (t) => { + const { metricAggregator } = t.context + t.equal(metricAggregator.empty, true) + t.end() }) - describe('started', () => { - it('should reflect when new metric collection started', () => { - expect(metricAggregator.started).to.equal(metricAggregator._metrics.started) - }) + t.test('should be false when metrics added', (t) => { + const { metricAggregator } = t.context + metricAggregator.getOrCreateMetric('myMetric') + t.equal(metricAggregator.empty, false) + t.end() }) - describe('_getMergeData()', () => { - it('should return mergable metric collection', () => { - metricAggregator.getOrCreateMetric('metric1', 'scope1') - metricAggregator.getOrCreateMetric('metric2') + t.test('should reflect when new metric collection started', (t) => { + const { metricAggregator } = t.context + t.equal(metricAggregator.started, metricAggregator._metrics.started) + t.end() + }) - const data = metricAggregator._getMergeData() - expect(data).to.have.property('started') + t.test('_getMergeData() should return mergable metric collection', (t) => { + const { metricAggregator } = t.context + metricAggregator.getOrCreateMetric('metric1', 'scope1') + metricAggregator.getOrCreateMetric('metric2') - expect(data.empty).to.be.false + const data = metricAggregator._getMergeData() + t.ok(data.started) + t.equal(data.empty, false) - const unscoped = data.unscoped - expect(unscoped).to.have.property('metric2') + const unscoped = data.unscoped + t.ok(unscoped.metric2) - const scoped = data.scoped - expect(scoped).to.have.property('scope1') + const scoped = data.scoped + t.ok(scoped.scope1) - expect(scoped.scope1).to.have.property('metric1') - }) + t.ok(scoped.scope1.metric1) + t.end() }) - describe('_toPayloadSync()', () => { - it('should return json format of data', () => { - const secondsToElapse = 5 + t.test('_toPayloadSync() should return json format of data', (t) => { + const { metricAggregator, testClock } = t.context + const secondsToElapse = 5 - const expectedMetricName = 'myMetric' - const expectedMetricScope = 'myScope' + const expectedMetricName = 'myMetric' + const expectedMetricScope = 'myScope' - metricAggregator - .getOrCreateMetric(expectedMetricName, expectedMetricScope) - .recordValue(22, 21) + metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(22, 21) - testClock.tick(secondsToElapse * 1000) + testClock.tick(secondsToElapse * 1000) - const expectedEndSeconds = EXPECTED_START_SECONDS + secondsToElapse + const expectedEndSeconds = EXPECTED_START_SECONDS + secondsToElapse - const payload = metricAggregator._toPayloadSync() + const payload = metricAggregator._toPayloadSync() - expect(payload.length).to.equal(4) + t.equal(payload.length, 4) - const [runId, startTime, endTime, metricData] = payload + const [runId, startTime, endTime, metricData] = payload - expect(runId).to.equal(RUN_ID) - expect(startTime).to.equal(EXPECTED_START_SECONDS) - expect(endTime).to.equal(expectedEndSeconds) + t.equal(runId, RUN_ID) + t.equal(startTime, EXPECTED_START_SECONDS) + t.equal(endTime, expectedEndSeconds) - const firstMetric = metricData[0] - expect(firstMetric.length).to.equal(2) + const firstMetric = metricData[0] + t.equal(firstMetric.length, 2) - const [metricName, metricStats] = firstMetric + const [metricName, metricStats] = firstMetric - expect(metricName).to.have.property('name', expectedMetricName) - expect(metricName).to.have.property('scope', expectedMetricScope) + t.equal(metricName.name, expectedMetricName) + t.equal(metricName.scope, expectedMetricScope) - // Before sending, we rely on the Stats toJSON to put in the right format - expect(metricStats.toJSON()).to.deep.equal([1, 22, 21, 22, 22, 484]) - }) + // Before sending, we rely on the Stats toJSON to put in the right format + t.same(metricStats.toJSON(), [1, 22, 21, 22, 22, 484]) + t.end() }) - describe('_toPayload()', () => { - it('should return json format of data', () => { - const secondsToElapse = 5 + t.test('_toPayload() should return json format of data', (t) => { + const { metricAggregator, testClock } = t.context + const secondsToElapse = 5 - const expectedMetricName = 'myMetric' - const expectedMetricScope = 'myScope' + const expectedMetricName = 'myMetric' + const expectedMetricScope = 'myScope' - metricAggregator - .getOrCreateMetric(expectedMetricName, expectedMetricScope) - .recordValue(22, 21) + metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(22, 21) - testClock.tick(secondsToElapse * 1000) + testClock.tick(secondsToElapse * 1000) - const expectedEndSeconds = EXPECTED_START_SECONDS + secondsToElapse + const expectedEndSeconds = EXPECTED_START_SECONDS + secondsToElapse - metricAggregator._toPayload((err, payload) => { - expect(payload.length).to.equal(4) + metricAggregator._toPayload((err, payload) => { + t.equal(payload.length, 4) - const [runId, startTime, endTime, metricData] = payload + const [runId, startTime, endTime, metricData] = payload - expect(runId).to.equal(RUN_ID) - expect(startTime).to.equal(EXPECTED_START_SECONDS) - expect(endTime).to.equal(expectedEndSeconds) + t.equal(runId, RUN_ID) + t.equal(startTime, EXPECTED_START_SECONDS) + t.equal(endTime, expectedEndSeconds) - const firstMetric = metricData[0] - expect(firstMetric.length).to.equal(2) + const firstMetric = metricData[0] + t.equal(firstMetric.length, 2) - const [metricName, metricStats] = firstMetric + const [metricName, metricStats] = firstMetric - expect(metricName).to.have.property('name', expectedMetricName) - expect(metricName).to.have.property('scope', expectedMetricScope) + t.equal(metricName.name, expectedMetricName) + t.equal(metricName.scope, expectedMetricScope) - // Before sending, we rely on the Stats toJSON to put in the right format - expect(metricStats.toJSON()).to.deep.equal([1, 22, 21, 22, 22, 484]) - }) + // Before sending, we rely on the Stats toJSON to put in the right format + t.same(metricStats.toJSON(), [1, 22, 21, 22, 22, 484]) + t.end() }) }) - describe('_merge()', () => { - it('should merge passed in metrics', () => { - const expectedMetricName = 'myMetric' - const expectedMetricScope = 'myScope' + t.test('_merge() should merge passed in metrics', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + const expectedMetricName = 'myMetric' + const expectedMetricScope = 'myScope' - metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(2, 1) + metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(2, 1) - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(4, 2) + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(4, 2) - mergeData.getOrCreateMetric('newMetric').incrementCallCount() + mergeData.getOrCreateMetric('newMetric').incrementCallCount() - metricAggregator._merge(mergeData) + metricAggregator._merge(mergeData) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - const newUnscopedMetric = metricAggregator.getMetric('newMetric') - expect(newUnscopedMetric).to.have.property('callCount', 1) + const newUnscopedMetric = metricAggregator.getMetric('newMetric') + t.equal(newUnscopedMetric.callCount, 1) - const mergedScopedMetric = metricAggregator.getMetric(expectedMetricName, expectedMetricScope) + const mergedScopedMetric = metricAggregator.getMetric(expectedMetricName, expectedMetricScope) - expect(mergedScopedMetric.callCount).to.equal(2) - expect(mergedScopedMetric.min).to.equal(2) - expect(mergedScopedMetric.max).to.equal(4) - expect(mergedScopedMetric.total).to.equal(6) - expect(mergedScopedMetric.totalExclusive).to.equal(3) - expect(mergedScopedMetric.sumOfSquares).to.equal(20) - }) + t.equal(mergedScopedMetric.callCount, 2) + t.equal(mergedScopedMetric.min, 2) + t.equal(mergedScopedMetric.max, 4) + t.equal(mergedScopedMetric.total, 6) + t.equal(mergedScopedMetric.totalExclusive, 3) + t.equal(mergedScopedMetric.sumOfSquares, 20) + t.end() + }) - it('should choose the lowest started', () => { - metricAggregator.getOrCreateMetric('metric1').incrementCallCount() + t.test('_merge() should choose the lowest started', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + metricAggregator.getOrCreateMetric('metric1').incrementCallCount() - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric('metric2').incrementCallCount() + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric('metric2').incrementCallCount() - // Artificially move start of merge data - mergeData.started = metricAggregator.started - 10 + // Artificially move start of merge data + mergeData.started = metricAggregator.started - 10 - metricAggregator._merge(mergeData) + metricAggregator._merge(mergeData) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - expect(metricAggregator.started).to.equal(mergeData.started) - }) + t.equal(metricAggregator.started, mergeData.started) + t.end() }) - describe('clear()', () => { - it('should clear metrics', () => { - metricAggregator.getOrCreateMetric('metric1', 'scope1').incrementCallCount() - metricAggregator.getOrCreateMetric('metric2').incrementCallCount() + t.test('clear() should clear metrics', (t) => { + const { metricAggregator } = t.context + metricAggregator.getOrCreateMetric('metric1', 'scope1').incrementCallCount() + metricAggregator.getOrCreateMetric('metric2').incrementCallCount() - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - metricAggregator.clear() + metricAggregator.clear() - expect(metricAggregator.empty).to.be.true + t.equal(metricAggregator.empty, true) - const metric1 = metricAggregator.getMetric('metric1', 'scope1') - expect(metric1).to.not.exist + const metric1 = metricAggregator.getMetric('metric1', 'scope1') + t.notOk(metric1) - const metric2 = metricAggregator.getMetric('metric2') - expect(metric2).to.not.exist - }) + const metric2 = metricAggregator.getMetric('metric2') + t.notOk(metric2) + t.end() + }) - it('should reset started', () => { - const msToElapse = 5000 + t.test('clear() should reset started', (t) => { + const { metricAggregator, testClock } = t.context + const msToElapse = 5000 - const originalStarted = metricAggregator.started + const originalStarted = metricAggregator.started - metricAggregator.getOrCreateMetric('metric1', 'scope1').incrementCallCount() - metricAggregator.getOrCreateMetric('metric2').incrementCallCount() + metricAggregator.getOrCreateMetric('metric1', 'scope1').incrementCallCount() + metricAggregator.getOrCreateMetric('metric2').incrementCallCount() - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - testClock.tick(msToElapse) + testClock.tick(msToElapse) - metricAggregator.clear() + metricAggregator.clear() - const newStarted = metricAggregator.started + const newStarted = metricAggregator.started - expect(newStarted).to.be.greaterThan(originalStarted) + t.ok(newStarted > originalStarted) - const expectedNewStarted = originalStarted + msToElapse - expect(newStarted).to.equal(expectedNewStarted) - }) + const expectedNewStarted = originalStarted + msToElapse + t.equal(newStarted, expectedNewStarted) + t.end() }) - describe('merge()', () => { - it('should merge passed in metrics', () => { - const expectedMetricName = 'myMetric' - const expectedMetricScope = 'myScope' + t.test('merge() should merge passed in metrics', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + const expectedMetricName = 'myMetric' + const expectedMetricScope = 'myScope' - metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(2, 1) + metricAggregator.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(2, 1) - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(4, 2) + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric(expectedMetricName, expectedMetricScope).recordValue(4, 2) - mergeData.getOrCreateMetric('newMetric').incrementCallCount() + mergeData.getOrCreateMetric('newMetric').incrementCallCount() - metricAggregator.merge(mergeData) + metricAggregator.merge(mergeData) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - const newUnscopedMetric = metricAggregator.getMetric('newMetric') - expect(newUnscopedMetric).to.have.property('callCount', 1) + const newUnscopedMetric = metricAggregator.getMetric('newMetric') + t.equal(newUnscopedMetric.callCount, 1) - const mergedScopedMetric = metricAggregator.getMetric(expectedMetricName, expectedMetricScope) + const mergedScopedMetric = metricAggregator.getMetric(expectedMetricName, expectedMetricScope) - expect(mergedScopedMetric.callCount).to.equal(2) - expect(mergedScopedMetric.min).to.equal(2) - expect(mergedScopedMetric.max).to.equal(4) - expect(mergedScopedMetric.total).to.equal(6) - expect(mergedScopedMetric.totalExclusive).to.equal(3) - expect(mergedScopedMetric.sumOfSquares).to.equal(20) - }) + t.equal(mergedScopedMetric.callCount, 2) + t.equal(mergedScopedMetric.min, 2) + t.equal(mergedScopedMetric.max, 4) + t.equal(mergedScopedMetric.total, 6) + t.equal(mergedScopedMetric.totalExclusive, 3) + t.equal(mergedScopedMetric.sumOfSquares, 20) + t.end() + }) - it('should not adjust start time when not passed', () => { - const originalStarted = metricAggregator.started + t.test('merge() should not adjust start time when not passed', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + const originalStarted = metricAggregator.started - metricAggregator.getOrCreateMetric('metric1').incrementCallCount() + metricAggregator.getOrCreateMetric('metric1').incrementCallCount() - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric('metric2').incrementCallCount() + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric('metric2').incrementCallCount() - // Artificially move start of merge data - mergeData.started = metricAggregator.started - 10 + // Artificially move start of merge data + mergeData.started = metricAggregator.started - 10 - metricAggregator.merge(mergeData) + metricAggregator.merge(mergeData) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - expect(metricAggregator.started).to.equal(originalStarted) - }) + t.equal(metricAggregator.started, originalStarted) + t.end() + }) - it('should not adjust start time when adjustStartTime false', () => { - const originalStarted = metricAggregator.started + t.test('merge() should not adjust start time when adjustStartTime false', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + const originalStarted = metricAggregator.started - metricAggregator.getOrCreateMetric('metric1').incrementCallCount() + metricAggregator.getOrCreateMetric('metric1').incrementCallCount() - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric('metric2').incrementCallCount() + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric('metric2').incrementCallCount() - // Artificially move start of merge data - mergeData.started = metricAggregator.started - 10 + // Artificially move start of merge data + mergeData.started = metricAggregator.started - 10 - metricAggregator.merge(mergeData, false) + metricAggregator.merge(mergeData, false) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - expect(metricAggregator.started).to.equal(originalStarted) - }) + t.equal(metricAggregator.started, originalStarted) + t.end() + }) - it('should choose lowest started when adjustStartTime true', () => { - metricAggregator.getOrCreateMetric('metric1').incrementCallCount() + t.test('merge() should choose lowest started when adjustStartTime true', (t) => { + const { metricAggregator, mapper, normalizer } = t.context + metricAggregator.getOrCreateMetric('metric1').incrementCallCount() - const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) - mergeData.getOrCreateMetric('metric2').incrementCallCount() + const mergeData = new Metrics(EXPECTED_APDEX_T, mapper, normalizer) + mergeData.getOrCreateMetric('metric2').incrementCallCount() - // Artificially move start of merge data - mergeData.started = metricAggregator.started - 10 + // Artificially move start of merge data + mergeData.started = metricAggregator.started - 10 - metricAggregator.merge(mergeData, true) + metricAggregator.merge(mergeData, true) - expect(metricAggregator.empty).to.be.false + t.equal(metricAggregator.empty, false) - expect(metricAggregator.started).to.equal(mergeData.started) - }) + t.equal(metricAggregator.started, mergeData.started) + t.end() }) - describe('getOrCreateMetric()', () => { - it('should return value from metrics collection', () => { - const spy = sinon.spy(metricAggregator._metrics, 'getOrCreateMetric') + t.test('getOrCreateMetric() should return value from metrics collection', (t) => { + const { metricAggregator } = t.context + const spy = sinon.spy(metricAggregator._metrics, 'getOrCreateMetric') - const metric = metricAggregator.getOrCreateMetric('newMetric') - metric.incrementCallCount() + const metric = metricAggregator.getOrCreateMetric('newMetric') + metric.incrementCallCount() - expect(metric).to.have.property('callCount', 1) + t.equal(metric.callCount, 1) - expect(spy.calledOnce).to.be.true - }) + t.equal(spy.calledOnce, true) + t.end() }) - describe('measureMilliseconds', () => { - it('should return value from metrics collection', () => { - const spy = sinon.spy(metricAggregator._metrics, 'measureMilliseconds') + t.test('measureMilliseconds should return value from metrics collection', (t) => { + const { metricAggregator } = t.context + const spy = sinon.spy(metricAggregator._metrics, 'measureMilliseconds') - const metric = metricAggregator.measureMilliseconds('metric', 'scope', 2000, 1000) + const metric = metricAggregator.measureMilliseconds('metric', 'scope', 2000, 1000) - expect(metric).to.exist + t.ok(metric) - expect(metric).to.have.property('callCount', 1) - expect(metric).to.have.property('total', 2) - expect(metric).to.have.property('totalExclusive', 1) + t.equal(metric.callCount, 1) + t.equal(metric.total, 2) + t.equal(metric.totalExclusive, 1) - expect(spy.calledOnce).to.be.true - }) + t.equal(spy.calledOnce, true) + t.end() }) - describe('measureBytes', () => { - const MEGABYTE = 1024 * 1024 + t.test('measureBytes should return value from metrics collection', (t) => { + const { metricAggregator } = t.context + const spy = sinon.spy(metricAggregator._metrics, 'measureBytes') - it('should return value from metrics collection', () => { - const spy = sinon.spy(metricAggregator._metrics, 'measureBytes') + const metric = metricAggregator.measureBytes('metric', MEGABYTE) - const metric = metricAggregator.measureBytes('metric', MEGABYTE) + t.ok(metric) - expect(metric).to.exist + t.equal(metric.callCount, 1) + t.equal(metric.total, 1) + t.equal(metric.totalExclusive, 1) - expect(metric).to.have.property('callCount', 1) - expect(metric).to.have.property('total', 1) - expect(metric).to.have.property('totalExclusive', 1) - - expect(spy.calledOnce).to.be.true - }) + t.equal(spy.calledOnce, true) + t.end() + }) - it('should record exclusive bytes', () => { - const metric = metricAggregator.measureBytes('metric', MEGABYTE * 2, MEGABYTE) + t.test('measureBytes should record exclusive bytes', (t) => { + const { metricAggregator } = t.context + const metric = metricAggregator.measureBytes('metric', MEGABYTE * 2, MEGABYTE) - expect(metric).to.exist + t.ok(metric) - expect(metric).to.have.property('callCount', 1) - expect(metric).to.have.property('total', 2) - expect(metric).to.have.property('totalExclusive', 1) - }) + t.equal(metric.callCount, 1) + t.equal(metric.total, 2) + t.equal(metric.totalExclusive, 1) + t.end() + }) - it('should optionally not convert to megabytes', () => { - const metric = metricAggregator.measureBytes('metric', 2, 1, true) + t.test('measureBytes should optionally not convert to megabytes', (t) => { + const { metricAggregator } = t.context + const metric = metricAggregator.measureBytes('metric', 2, 1, true) - expect(metric).to.exist + t.ok(metric) - expect(metric).to.have.property('callCount', 1) - expect(metric).to.have.property('total', 2) - expect(metric).to.have.property('totalExclusive', 1) - }) + t.equal(metric.callCount, 1) + t.equal(metric.total, 2) + t.equal(metric.totalExclusive, 1) + t.end() }) - describe('getMetric()', () => { - it('should return value from metrics collection', () => { - const expectedName = 'name1' - const expectedScope = 'scope1' + t.test('getMetric() should return value from metrics collection', (t) => { + const { metricAggregator } = t.context + const expectedName = 'name1' + const expectedScope = 'scope1' - const spy = sinon.spy(metricAggregator._metrics, 'getMetric') + const spy = sinon.spy(metricAggregator._metrics, 'getMetric') - metricAggregator.getOrCreateMetric(expectedName, expectedScope).incrementCallCount() + metricAggregator.getOrCreateMetric(expectedName, expectedScope).incrementCallCount() - const metric = metricAggregator.getMetric(expectedName, expectedScope) + const metric = metricAggregator.getMetric(expectedName, expectedScope) - expect(metric).to.exist - expect(metric).to.have.property('callCount', 1) + t.ok(metric) + t.equal(metric.callCount, 1) - expect(spy.calledOnce).to.be.true - }) + t.equal(spy.calledOnce, true) + t.end() }) - describe('getOrCreateApdexMetric()', () => { - it('should return value from metrics collection', () => { - const spy = sinon.spy(metricAggregator._metrics, 'getOrCreateApdexMetric') + t.test('getOrCreateApdexMetric() should return value from metrics collection', (t) => { + const { metricAggregator } = t.context + const spy = sinon.spy(metricAggregator._metrics, 'getOrCreateApdexMetric') - const metric = metricAggregator.getOrCreateApdexMetric('metric1', 'scope1') + const metric = metricAggregator.getOrCreateApdexMetric('metric1', 'scope1') - expect(metric).to.have.property('apdexT', EXPECTED_APDEX_T) + t.equal(metric.apdexT, EXPECTED_APDEX_T) - expect(spy.calledOnce).to.be.true - }) + t.equal(spy.calledOnce, true) + t.end() }) + t.end() }) diff --git a/test/unit/metric/metrics.test.js b/test/unit/metric/metrics.test.js index 702ecf37d7..8dd7c65d9a 100644 --- a/test/unit/metric/metrics.test.js +++ b/test/unit/metric/metrics.test.js @@ -4,274 +4,353 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../../lib/agent_helper') const Metrics = require('../../../lib/metrics') const MetricMapper = require('../../../lib/metrics/mapper') const MetricNormalizer = require('../../../lib/metrics/normalizer') -describe('Metrics', function () { - let metrics - let agent - - beforeEach(function () { - agent = helper.loadMockedAgent() - metrics = new Metrics(agent.config.apdex_t, agent.mapper, agent.metricNameNormalizer) - }) - - afterEach(function () { - helper.unloadAgent(agent) - }) - - describe('when creating', function () { - it('should throw if apdexT is not set', function () { - expect(function () { - metrics = new Metrics(undefined, agent.mapper, agent.metricNameNormalizer) - }).throws() +function beforeEach(t) { + const agent = helper.loadMockedAgent() + t.context.metrics = new Metrics(agent.config.apdex_t, agent.mapper, agent.metricNameNormalizer) + t.context.agent = agent +} + +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} + +tap.test('Metrics', function (t) { + t.autoend() + t.test('when creating', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should throw if apdexT is not set', function (t) { + const { agent } = t.context + t.throws(function () { + // eslint-disable-next-line no-new + new Metrics(undefined, agent.mapper, agent.metricNameNormalizer) + }) + t.end() }) - it('should throw if no name -> ID mapper is provided', function () { - expect(function () { - metrics = new Metrics(agent.config.apdex_t, undefined, agent.metricNameNormalizer) - }).throws() + t.test('should throw if no name -> ID mapper is provided', function (t) { + const { agent } = t.context + t.throws(function () { + // eslint-disable-next-line no-new + new Metrics(agent.config.apdex_t, undefined, agent.metricNameNormalizer) + }) + t.end() }) - it('should throw if no metric name normalizer is provided', function () { - expect(function () { - metrics = new Metrics(agent.config.apdex_t, agent.mapper, undefined) - }).throws() + t.test('should throw if no metric name normalizer is provided', function (t) { + const { agent } = t.context + t.throws(function () { + // eslint-disable-next-line no-new + new Metrics(agent.config.apdex_t, agent.mapper, undefined) + }) + t.end() }) - it('should return apdex summaries with an apdexT same as config', function () { + t.test('should return apdex summaries with an apdexT same as config', function (t) { + const { metrics, agent } = t.context const metric = metrics.getOrCreateApdexMetric('Apdex/MetricsTest') - expect(metric.apdexT).equal(agent.config.apdex_t) + t.equal(metric.apdexT, agent.config.apdex_t) + t.end() }) - it('should allow overriding apdex summaries with a custom apdexT', function () { + t.test('should allow overriding apdex summaries with a custom apdexT', function (t) { + const { metrics } = t.context const metric = metrics.getOrCreateApdexMetric('Apdex/MetricsTest', null, 1) - expect(metric.apdexT).equal(0.001) + t.equal(metric.apdexT, 0.001) + t.end() }) - it('should require the overriding apdex to be greater than 0', function () { + t.test('should require the overriding apdex to be greater than 0', function (t) { + const { metrics, agent } = t.context const metric = metrics.getOrCreateApdexMetric('Apdex/MetricsTest', null, 0) - expect(metric.apdexT).equal(agent.config.apdex_t) + t.equal(metric.apdexT, agent.config.apdex_t) + t.end() }) - it('should require the overriding apdex to not be negative', function () { + t.test('should require the overriding apdex to not be negative', function (t) { + const { metrics, agent } = t.context const metric = metrics.getOrCreateApdexMetric('Apdex/MetricsTest', null, -5000) - expect(metric.apdexT).equal(agent.config.apdex_t) + t.equal(metric.apdexT, agent.config.apdex_t) + t.end() }) - }) - describe('when creating with parameters', function () { - const TEST_APDEX = 0.4 - const TEST_MAPPER = new MetricMapper([[{ name: 'Renamed/333' }, 1337]]) - const TEST_NORMALIZER = new MetricNormalizer({ enforce_backstop: true }, 'metric name') + t.test('when creating individual apdex metrics should have apdex functions', function (t) { + const { metrics } = t.context + const metric = metrics.getOrCreateApdexMetric('Agent/ApdexTest') + t.ok(metric.incrementFrustrating) + t.end() + }) - beforeEach(function () { - TEST_NORMALIZER.addSimple(/^Test\/RenameMe(.*)$/, 'Renamed/$1') - metrics = new Metrics(TEST_APDEX, TEST_MAPPER, TEST_NORMALIZER) + t.test('should measure an unscoped metric', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/Metric', null, 400, 200) + t.equal( + JSON.stringify(metrics.toJSON()), + '[[{"name":"Test/Metric"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' + ) + t.end() }) - it('should pass apdex through to ApdexStats', function () { - const apdex = metrics.getOrCreateApdexMetric('Test/RenameMe333') - expect(apdex.apdexT).equal(TEST_APDEX) + t.test('should measure a scoped metric', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('T/M', 'T', 400, 200) + t.equal( + JSON.stringify(metrics.toJSON()), + '[[{"name":"T/M","scope":"T"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' + ) + t.end() }) - it('should pass metric mappings through for serialization', function () { - metrics.measureMilliseconds('Test/RenameMe333', null, 400, 300) - const summary = JSON.stringify(metrics.toJSON()) - expect(summary).equal('[[1337,[1,0.4,0.3,0.4,0.4,0.16000000000000003]]]') + t.test('should resolve the correctly scoped set of metrics when scope passed', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Apdex/ScopedMetricsTest', 'TEST') + const scoped = metrics._resolve('TEST') + + t.ok(scoped['Apdex/ScopedMetricsTest']) + t.end() + }) + + t.test('should implicitly create a blank set of metrics when resolving new scope', (t) => { + const { metrics } = t.context + const scoped = metrics._resolve('NOEXISTBRO') + + t.ok(scoped) + t.equal(Object.keys(scoped).length, 0) + t.end() + }) + + t.test('should return a preëxisting unscoped metric when it is requested', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) + t.equal(metrics.getOrCreateMetric('Test/UnscopedMetric').callCount, 1) + t.end() + }) + + t.test('should return a preëxisting scoped metric when it is requested', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/Metric', 'TEST', 400, 200) + t.equal(metrics.getOrCreateMetric('Test/Metric', 'TEST').callCount, 1) + t.end() + }) + + t.test('should return the unscoped metrics when scope not set', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) + t.equal(Object.keys(metrics._resolve()).length, 1) + t.equal(Object.keys(metrics.scoped).length, 0) + t.end() + }) + + t.test('should measure bytes ok', function (t) { + const { metrics } = t.context + const MEGABYTE = 1024 * 1024 + const stat = metrics.measureBytes('Test/Bytes', MEGABYTE) + t.equal(stat.total, 1) + t.equal(stat.totalExclusive, 1) + t.end() + }) + + t.test('should measure exclusive bytes ok', function (t) { + const { metrics } = t.context + const MEGABYTE = 1024 * 1024 + const stat = metrics.measureBytes('Test/Bytes', MEGABYTE * 2, MEGABYTE) + t.equal(stat.total, 2) + t.equal(stat.totalExclusive, 1) + t.end() + }) + + t.test('should optionally not convert bytes to megabytes', function (t) { + const { metrics } = t.context + const MEGABYTE = 1024 * 1024 + const stat = metrics.measureBytes('Test/Bytes', MEGABYTE * 2, MEGABYTE, true) + t.equal(stat.total, MEGABYTE * 2) + t.equal(stat.totalExclusive, MEGABYTE) + t.end() }) }) - describe('when creating individual metrics', function () { - it('should create a metric when a nonexistent name is requested', function () { + t.test('when creating individual metrics', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should create a metric when a nonexistent name is requested', function (t) { + const { metrics } = t.context const metric = metrics.getOrCreateMetric('Test/Nonexistent', 'TEST') - expect(metric).to.have.property('callCount') + t.equal(metric.callCount, 0) + t.end() }) - it('should have statistics available', function () { + t.test('should have statistics available', function (t) { + const { metrics } = t.context const metric = metrics.getOrCreateMetric('Agent/Test') - expect(metric).to.have.property('callCount') + t.equal(metric.callCount, 0) + t.end() }) - it('should have have regular functions', function () { + t.test('should have have regular functions', function (t) { + const { metrics } = t.context const metric = metrics.getOrCreateMetric('Agent/StatsTest') - expect(metric).to.have.property('incrementCallCount') + t.equal(metric.callCount, 0) + t.end() }) }) - describe('when creating individual apdex metrics', function () { - it('should have apdex functions', function () { - const metric = metrics.getOrCreateApdexMetric('Agent/ApdexTest') - expect(metric).to.have.property('incrementFrustrating') + t.test('when creating with parameters', function (t) { + t.autoend() + const TEST_APDEX = 0.4 + const TEST_MAPPER = new MetricMapper([[{ name: 'Renamed/333' }, 1337]]) + const TEST_NORMALIZER = new MetricNormalizer({ enforce_backstop: true }, 'metric name') + + t.beforeEach(function (t) { + beforeEach(t) + TEST_NORMALIZER.addSimple(/^Test\/RenameMe(.*)$/, 'Renamed/$1') + t.context.metrics = new Metrics(TEST_APDEX, TEST_MAPPER, TEST_NORMALIZER) }) - }) - it('should measure an unscoped metric', function () { - metrics.measureMilliseconds('Test/Metric', null, 400, 200) - expect(JSON.stringify(metrics.toJSON())).equal( - '[[{"name":"Test/Metric"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' - ) - }) + t.afterEach(afterEach) + + t.test('should pass apdex through to ApdexStats', function (t) { + const { metrics } = t.context + const apdex = metrics.getOrCreateApdexMetric('Test/RenameMe333') + t.equal(apdex.apdexT, TEST_APDEX) + t.end() + }) - it('should measure a scoped metric', function () { - metrics.measureMilliseconds('T/M', 'T', 400, 200) - expect(JSON.stringify(metrics.toJSON())).equal( - '[[{"name":"T/M","scope":"T"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' - ) + t.test('should pass metric mappings through for serialization', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/RenameMe333', null, 400, 300) + const summary = JSON.stringify(metrics.toJSON()) + t.equal(summary, '[[1337,[1,0.4,0.3,0.4,0.4,0.16000000000000003]]]') + t.end() + }) }) - it('should resolve the correctly scoped set of metrics when scope passed', function () { - metrics.measureMilliseconds('Apdex/ScopedMetricsTest', 'TEST') - const scoped = metrics._resolve('TEST') + t.test('with ordinary statistics', function (t) { + t.autoend() + const NAME = 'Agent/Test384' - expect(scoped['Apdex/ScopedMetricsTest']).an('object') - }) + t.beforeEach(function (t) { + beforeEach(t) + const metric = t.context.metrics.getOrCreateMetric(NAME) + const mapper = new MetricMapper([[{ name: NAME }, 1234]]) + t.context.metric = metric + t.context.mapper = mapper + }) - it('should implicitly create a blank set of metrics when resolving new scope', () => { - const scoped = metrics._resolve('NOEXISTBRO') + t.afterEach(afterEach) - expect(scoped).an('object') - expect(Object.keys(scoped).length).equal(0) - }) + t.test('should get the bare stats right', function (t) { + const { metrics } = t.context + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[{"name":"Agent/Test384"},[0,0,0,0,0,0]]') + t.end() + }) - it('should return a preëxisting unscoped metric when it is requested', function () { - metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) - expect(metrics.getOrCreateMetric('Test/UnscopedMetric').callCount).equal(1) - }) + t.test('should correctly map metrics to IDs given a mapping', function (t) { + const { metrics, mapper } = t.context + metrics.mapper = mapper + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[1234,[0,0,0,0,0,0]]') + t.end() + }) - it('should return a preëxisting scoped metric when it is requested', function () { - metrics.measureMilliseconds('Test/Metric', 'TEST', 400, 200) - expect(metrics.getOrCreateMetric('Test/Metric', 'TEST').callCount).equal(1) + t.test('should correctly serialize statistics', function (t) { + const { metrics, metric } = t.context + metric.recordValue(0.3, 0.1) + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[{"name":"Agent/Test384"},[1,0.3,0.1,0.3,0.3,0.09]]') + t.end() + }) }) - it('should return the unscoped metrics when scope not set', function () { - metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) - expect(Object.keys(metrics._resolve()).length).equal(1) - expect(Object.keys(metrics.scoped).length).equal(0) - }) + t.test('with apdex statistics', function (t) { + t.autoend() + const NAME = 'Agent/Test385' + t.beforeEach(function (t) { + beforeEach(t) + const { agent } = t.context + const metrics = new Metrics(0.8, new MetricMapper(), agent.metricNameNormalizer) + t.context.metric = metrics.getOrCreateApdexMetric(NAME) + t.context.mapper = new MetricMapper([[{ name: NAME }, 1234]]) + t.context.metrics = metrics + }) - it('should measure bytes ok', function () { - const MEGABYTE = 1024 * 1024 - const stat = metrics.measureBytes('Test/Bytes', MEGABYTE) - expect(stat.total).equal(1) - expect(stat.totalExclusive).equal(1) - }) + t.afterEach(afterEach) - it('should measure exclusive bytes ok', function () { - const MEGABYTE = 1024 * 1024 - const stat = metrics.measureBytes('Test/Bytes', MEGABYTE * 2, MEGABYTE) - expect(stat.total).equal(2) - expect(stat.totalExclusive).equal(1) - }) + t.test('should get the bare stats right', function (t) { + const { metrics } = t.context + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[{"name":"Agent/Test385"},[0,0,0,0.8,0.8,0]]') + t.end() + }) - it('should optionally not convert bytes to megabytes', function () { - const MEGABYTE = 1024 * 1024 - const stat = metrics.measureBytes('Test/Bytes', MEGABYTE * 2, MEGABYTE, true) - expect(stat.total).equal(MEGABYTE * 2) - expect(stat.totalExclusive).equal(MEGABYTE) + t.test('should correctly map metrics to IDs given a mapping', function (t) { + const { metrics, mapper } = t.context + metrics.mapper = mapper + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[1234,[0,0,0,0.8,0.8,0]]') + t.end() + }) + + t.test('should correctly serialize statistics', function (t) { + const { metric, metrics } = t.context + metric.recordValueInMillis(3220) + const summary = JSON.stringify(metrics._getUnscopedData(NAME)) + t.equal(summary, '[{"name":"Agent/Test385"},[0,0,1,0.8,0.8,0]]') + t.end() + }) }) - describe('when serializing', function () { - describe('unscoped metrics', function () { - it('should get the basics right', function () { - metrics.measureMilliseconds('Test/Metric', null, 400, 200) - metrics.measureMilliseconds('RenameMe333', null, 400, 300) - metrics.measureMilliseconds('Test/ScopedMetric', 'TEST', 400, 200) - - expect(JSON.stringify(metrics._toUnscopedData())).to.equal( - '[[{"name":"Test/Metric"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]],' + - '[{"name":"RenameMe333"},[1,0.4,0.3,0.4,0.4,0.16000000000000003]]]' - ) - }) + t.test('scoped metrics', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) - describe('with ordinary statistics', function () { - const NAME = 'Agent/Test384' - let metric - let mapper - - beforeEach(function () { - metric = metrics.getOrCreateMetric(NAME) - mapper = new MetricMapper([[{ name: NAME }, 1234]]) - }) - - it('should get the bare stats right', function () { - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).to.equal('[{"name":"Agent/Test384"},[0,0,0,0,0,0]]') - }) - - it('should correctly map metrics to IDs given a mapping', function () { - metrics.mapper = mapper - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).equal('[1234,[0,0,0,0,0,0]]') - }) - - it('should correctly serialize statistics', function () { - metric.recordValue(0.3, 0.1) - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).to.equal('[{"name":"Agent/Test384"},[1,0.3,0.1,0.3,0.3,0.09]]') - }) - }) + t.test('when serializing unscoped metrics should get the basics right', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/Metric', null, 400, 200) + metrics.measureMilliseconds('RenameMe333', null, 400, 300) + metrics.measureMilliseconds('Test/ScopedMetric', 'TEST', 400, 200) - describe('with apdex statistics', function () { - const NAME = 'Agent/Test385' - let metric - let mapper - - beforeEach(function () { - metrics = new Metrics(0.8, new MetricMapper(), agent.metricNameNormalizer) - metric = metrics.getOrCreateApdexMetric(NAME) - mapper = new MetricMapper([[{ name: NAME }, 1234]]) - }) - - it('should get the bare stats right', function () { - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).to.equal('[{"name":"Agent/Test385"},[0,0,0,0.8,0.8,0]]') - }) - - it('should correctly map metrics to IDs given a mapping', function () { - metrics.mapper = mapper - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).equal('[1234,[0,0,0,0.8,0.8,0]]') - }) - - it('should correctly serialize statistics', function () { - metric.recordValueInMillis(3220) - const summary = JSON.stringify(metrics._getUnscopedData(NAME)) - expect(summary).to.equal('[{"name":"Agent/Test385"},[0,0,1,0.8,0.8,0]]') - }) - }) + t.equal( + JSON.stringify(metrics._toUnscopedData()), + '[[{"name":"Test/Metric"},[1,0.4,0.2,0.4,0.4,0.16000000000000003]],' + + '[{"name":"RenameMe333"},[1,0.4,0.3,0.4,0.4,0.16000000000000003]]]' + ) + t.end() }) - describe('scoped metrics', function () { - it('should get the basics right', function () { - metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) - metrics.measureMilliseconds('Test/RenameMe333', 'TEST', 400, 300) - metrics.measureMilliseconds('Test/ScopedMetric', 'ANOTHER', 400, 200) + t.test('should get the basics right', function (t) { + const { metrics } = t.context + metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) + metrics.measureMilliseconds('Test/RenameMe333', 'TEST', 400, 300) + metrics.measureMilliseconds('Test/ScopedMetric', 'ANOTHER', 400, 200) - expect(JSON.stringify(metrics._toScopedData())).to.equal( - '[[{"name":"Test/RenameMe333","scope":"TEST"},' + - '[1,0.4,0.3,0.4,0.4,0.16000000000000003]],' + - '[{"name":"Test/ScopedMetric","scope":"ANOTHER"},' + - '[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' - ) - }) + t.equal( + JSON.stringify(metrics._toScopedData()), + '[[{"name":"Test/RenameMe333","scope":"TEST"},' + + '[1,0.4,0.3,0.4,0.4,0.16000000000000003]],' + + '[{"name":"Test/ScopedMetric","scope":"ANOTHER"},' + + '[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' + ) + t.end() }) - it('should serialize correctly', function () { + t.test('should serialize correctly', function (t) { + const { metrics } = t.context metrics.measureMilliseconds('Test/UnscopedMetric', null, 400, 200) metrics.measureMilliseconds('Test/RenameMe333', null, 400, 300) metrics.measureMilliseconds('Test/ScopedMetric', 'TEST', 400, 200) - expect(JSON.stringify(metrics.toJSON())).to.equal( + t.equal( + JSON.stringify(metrics.toJSON()), '[[{"name":"Test/UnscopedMetric"},' + '[1,0.4,0.2,0.4,0.4,0.16000000000000003]],' + '[{"name":"Test/RenameMe333"},' + @@ -279,20 +358,22 @@ describe('Metrics', function () { '[{"name":"Test/ScopedMetric","scope":"TEST"},' + '[1,0.4,0.2,0.4,0.4,0.16000000000000003]]]' ) + t.end() }) }) - describe('when merging two metrics collections', function () { - let other = null - - beforeEach(function () { + t.test('when merging two metrics collections', function (t) { + t.autoend() + t.beforeEach(function (t) { + beforeEach(t) + const { metrics, agent } = t.context metrics.started = 31337 metrics.measureMilliseconds('Test/Metrics/Unscoped', null, 400) metrics.measureMilliseconds('Test/Unscoped', null, 300) metrics.measureMilliseconds('Test/Scoped', 'METRICS', 200) metrics.measureMilliseconds('Test/Scoped', 'MERGE', 100) - other = new Metrics(agent.config.apdex_t, agent.mapper, agent.metricNameNormalizer) + const other = new Metrics(agent.config.apdex_t, agent.mapper, agent.metricNameNormalizer) other.started = 1337 other.measureMilliseconds('Test/Other/Unscoped', null, 800) other.measureMilliseconds('Test/Unscoped', null, 700) @@ -300,29 +381,40 @@ describe('Metrics', function () { other.measureMilliseconds('Test/Scoped', 'MERGE', 500) metrics.merge(other) + t.context.other = other }) - it('has all the metrics that were only in one', function () { - expect(metrics.getMetric('Test/Metrics/Unscoped').callCount).equal(1) - expect(metrics.getMetric('Test/Other/Unscoped').callCount).equal(1) - expect(metrics.getMetric('Test/Scoped', 'METRICS').callCount).equal(1) - expect(metrics.getMetric('Test/Scoped', 'OTHER').callCount).equal(1) + t.afterEach(afterEach) + + t.test('has all the metrics that were only in one', function (t) { + const { metrics } = t.context + t.equal(metrics.getMetric('Test/Metrics/Unscoped').callCount, 1) + t.equal(metrics.getMetric('Test/Other/Unscoped').callCount, 1) + t.equal(metrics.getMetric('Test/Scoped', 'METRICS').callCount, 1) + t.equal(metrics.getMetric('Test/Scoped', 'OTHER').callCount, 1) + t.end() }) - it('merged metrics that were in both', function () { - expect(metrics.getMetric('Test/Unscoped').callCount).equal(2) - expect(metrics.getMetric('Test/Scoped', 'MERGE').callCount).equal(2) + t.test('merged metrics that were in both', function (t) { + const { metrics } = t.context + t.equal(metrics.getMetric('Test/Unscoped').callCount, 2) + t.equal(metrics.getMetric('Test/Scoped', 'MERGE').callCount, 2) + t.end() }) - it('does not keep the earliest creation time', function () { - expect(metrics.started).to.equal(31337) + t.test('does not keep the earliest creation time', function (t) { + const { metrics } = t.context + t.equal(metrics.started, 31337) + t.end() }) - it('does keep the earliest creation time if told to', function () { + t.test('does keep the earliest creation time if told to', function (t) { + const { metrics, other } = t.context metrics.merge(other, true) - expect(metrics.started).to.equal(1337) + t.equal(metrics.started, 1337) + t.end() }) }) - it('should not let exclusive duration exceed total duration') + t.test('should not let exclusive duration exceed total duration', { todo: true }) }) diff --git a/test/unit/metric/normalizer-rule.test.js b/test/unit/metric/normalizer-rule.test.js index 9b386827fc..025c9f7b88 100644 --- a/test/unit/metric/normalizer-rule.test.js +++ b/test/unit/metric/normalizer-rule.test.js @@ -4,20 +4,14 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const Rule = require('../../../lib/metrics/normalizer/rule') -describe('NormalizerRule', function () { - let rule - - describe('with a very simple specification', function () { - before(function () { +tap.test('NormalizerRule', function (t) { + t.autoend() + t.test('with a very simple specification', function (t) { + t.autoend() + t.beforeEach(function (t) { // sample rule sent by staging collector 1 on 2012-08-29 const sample = { each_segment: false, @@ -29,92 +23,125 @@ describe('NormalizerRule', function () { replacement: '\\1' } - rule = new Rule(sample) + t.context.rule = new Rule(sample) }) - it('should know whether the rule terminates normalization', function () { - expect(rule.isTerminal).equal(true) + t.test('should know whether the rule terminates normalization', function (t) { + const { rule } = t.context + t.equal(rule.isTerminal, true) + t.end() }) - it('should know its own precedence', function () { - expect(rule.precedence).equal(0) + t.test('should know its own precedence', function (t) { + const { rule } = t.context + t.equal(rule.precedence, 0) + t.end() }) - it('should correctly compile the included regexp', function () { - expect(rule.matches('test_match_nothing')).equal(true) - expect(rule.matches('a test_match_nothing')).equal(false) - expect(rule.matches("test_match_nothin'")).equal(false) + t.test('should correctly compile the included regexp', function (t) { + const { rule } = t.context + t.equal(rule.matches('test_match_nothing'), true) + t.equal(rule.matches('a test_match_nothing'), false) + t.equal(rule.matches("test_match_nothin'"), false) + t.end() }) - it("shouldn't throw if the regexp doesn't compile", function () { + t.test("shouldn't throw if the regexp doesn't compile", function (t) { const whoops = { match_expression: '$[ad^' } let bad - expect(function () { + t.doesNotThrow(function () { bad = new Rule(whoops) - }).not.throws() - expect(bad.matches('')).equal(true) + }) + t.equal(bad.matches(''), true) + t.end() }) - it("should know if the regexp is applied to each 'segment' in the URL", function () { - expect(rule.eachSegment).equal(false) + t.test("should know if the regexp is applied to each 'segment' in the URL", function (t) { + const { rule } = t.context + t.equal(rule.eachSegment, false) + t.end() }) - it('should know if the regexp replaces all instances in the URL', function () { - expect(rule.replaceAll).equal(false) + t.test('should know if the regexp replaces all instances in the URL', function (t) { + const { rule } = t.context + t.equal(rule.replaceAll, false) + t.end() }) - it('should parse the replacement pattern', function () { - expect(rule.replacement).equal('$1') + t.test('should parse the replacement pattern', function (t) { + const { rule } = t.context + t.equal(rule.replacement, '$1') + t.end() }) - it('should know whether to ignore the URL', function () { - expect(rule.ignore).equal(false) + t.test('should know whether to ignore the URL', function (t) { + const { rule } = t.context + t.equal(rule.ignore, false) + t.end() }) - it('should be able to take in a non-normalized URL and return it normalized', () => { - expect(rule.apply('test_match_nothing')).equal('test_match_nothing') + t.test('should be able to take in a non-normalized URL and return it normalized', (t) => { + const { rule } = t.context + t.equal(rule.apply('test_match_nothing'), 'test_match_nothing') + t.end() }) }) - describe("with Saxon's patterns", function () { - describe("including '^(?!account|application).*'", function () { - beforeEach(function () { - rule = new Rule({ + t.test("with Saxon's patterns", function (t) { + t.autoend() + t.test("including '^(?!account|application).*'", function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.rule = new Rule({ each_segment: true, match_expression: '^(?!account|application).*', replacement: '*' }) }) - it("implies '/account/myacc/application/test' -> '/account/*/application/*'", function () { - expect(rule.apply('/account/myacc/application/test')).equal('/account/*/application/*') - }) - - it("implies '/oh/dude/account/myacc/application' -> '/*/*/account/*/application'", function () { - expect(rule.apply('/oh/dude/account/myacc/application')).equal('/*/*/account/*/application') - }) + t.test( + "implies '/account/myacc/application/test' -> '/account/*/application/*'", + function (t) { + const { rule } = t.context + t.equal(rule.apply('/account/myacc/application/test'), '/account/*/application/*') + t.end() + } + ) + + t.test( + "implies '/oh/dude/account/myacc/application' -> '/*/*/account/*/application'", + function (t) { + const { rule } = t.context + t.equal(rule.apply('/oh/dude/account/myacc/application'), '/*/*/account/*/application') + t.end() + } + ) }) const expression = '^(?!channel|download|popups|search|tap|user' + '|related|admin|api|genres|notification).*' - describe(`including '${expression}'`, function () { - beforeEach(function () { - rule = new Rule({ + t.test(`including '${expression}'`, function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.rule = new Rule({ each_segment: true, match_expression: expression, replacement: '*' }) }) - it("implies '/tap/stuff/user/gfy77t/view' -> '/tap/*/user/*/*'", function () { - expect(rule.apply('/tap/stuff/user/gfy77t/view')).equal('/tap/*/user/*/*') + t.test("implies '/tap/stuff/user/gfy77t/view' -> '/tap/*/user/*/*'", function (t) { + const { rule } = t.context + t.equal(rule.apply('/tap/stuff/user/gfy77t/view'), '/tap/*/user/*/*') + t.end() }) }) }) - describe('with a more complex substitution rule', function () { - before(function () { + t.test('with a more complex substitution rule', function (t) { + t.autoend() + t.beforeEach(function (t) { // sample rule sent by staging collector 1 on 2012-08-29 const sample = { each_segment: true, @@ -126,45 +153,61 @@ describe('NormalizerRule', function () { replacement: '*' } - rule = new Rule(sample) + t.context.rule = new Rule(sample) }) - it('should know whether the rule terminates normalization', function () { - expect(rule.isTerminal).equal(false) + t.test('should know whether the rule terminates normalization', function (t) { + const { rule } = t.context + t.equal(rule.isTerminal, false) + t.end() }) - it('should know its own precedence', function () { - expect(rule.precedence).equal(1) + t.test('should know its own precedence', function (t) { + const { rule } = t.context + t.equal(rule.precedence, 1) + t.end() }) - it('should correctly compile the included regexp', function () { - expect(rule.matches('/00dead_beef_00,b/hamburt')).equal(true) - expect(rule.matches('a test_match_nothing')).equal(false) - expect(rule.matches('/00 dead dad/nomatch')).equal(false) + t.test('should correctly compile the included regexp', function (t) { + const { rule } = t.context + t.equal(rule.matches('/00dead_beef_00,b/hamburt'), true) + t.equal(rule.matches('a test_match_nothing'), false) + t.equal(rule.matches('/00 dead dad/nomatch'), false) + t.end() }) - it("should know if the regexp is applied to each 'segment' in the URL", function () { - expect(rule.eachSegment).equal(true) + t.test("should know if the regexp is applied to each 'segment' in the URL", function (t) { + const { rule } = t.context + t.equal(rule.eachSegment, true) + t.end() }) - it('should know if the regexp replaces all instances in the URL', function () { - expect(rule.replaceAll).equal(false) + t.test('should know if the regexp replaces all instances in the URL', function (t) { + const { rule } = t.context + t.equal(rule.replaceAll, false) + t.end() }) - it('should parse the replacement pattern', function () { - expect(rule.replacement).equal('*') + t.test('should parse the replacement pattern', function (t) { + const { rule } = t.context + t.equal(rule.replacement, '*') + t.end() }) - it('should know whether to ignore the URL', function () { - expect(rule.ignore).equal(false) + t.test('should know whether to ignore the URL', function (t) { + const { rule } = t.context + t.equal(rule.ignore, false) + t.end() }) - it('should be able to take in a non-normalized URL and return it normalized', () => { - expect(rule.apply('/00dead_beef_00,b/hamburt')).equal('/*/hamburt') + t.test('should be able to take in a non-normalized URL and return it normalized', (t) => { + const { rule } = t.context + t.equal(rule.apply('/00dead_beef_00,b/hamburt'), '/*/hamburt') + t.end() }) }) - it('should replace all the instances of a pattern when so specified', function () { + t.test('should replace all the instances of a pattern when so specified', function (t) { const sample = { each_segment: false, eval_order: 0, @@ -174,54 +217,67 @@ describe('NormalizerRule', function () { ignore: false, replacement: 'y' } - rule = new Rule(sample) + const rule = new Rule(sample) - expect(rule.pattern.global).equal(true) - expect(rule.apply('/test/xXxxXx0xXxzxxxxXx')).equal('/test/yy0yzyy') + t.equal(rule.pattern.global, true) + t.equal(rule.apply('/test/xXxxXx0xXxzxxxxXx'), '/test/yy0yzyy') + t.end() }) - describe('when given an incomplete specification', function () { - it("shouldn't throw (but it can log!)", function () { - expect(function () { - rule = new Rule() - }).not.throws() + t.test('when given an incomplete specification', function (t) { + t.autoend() + t.test("shouldn't throw (but it can log!)", function (t) { + t.doesNotThrow(function () { + // eslint-disable-next-line no-new + new Rule() + }) + t.end() }) - it('should default to not applying the rule to each segment', function () { - expect(new Rule().eachSegment).equal(false) + t.test('should default to not applying the rule to each segment', function (t) { + t.equal(new Rule().eachSegment, false) + t.end() }) - it("should default the rule's precedence to 0", function () { - expect(new Rule().precedence).equal(0) + t.test("should default the rule's precedence to 0", function (t) { + t.equal(new Rule().precedence, 0) + t.end() }) - it('should default to not terminating rule evaluation', function () { - expect(new Rule().isTerminal).equal(false) + t.test('should default to not terminating rule evaluation', function (t) { + t.equal(new Rule().isTerminal, false) + t.end() }) - it('should have a regexp that matches the empty string', function () { - expect(new Rule().pattern).eql(/^$/i) + t.test('should have a regexp that matches the empty string', function (t) { + t.same(new Rule().pattern, /^$/i) + t.end() }) - it('should use the entire match as the replacement value', function () { - expect(new Rule().replacement).equal('$0') + t.test('should use the entire match as the replacement value', function (t) { + t.equal(new Rule().replacement, '$0') + t.end() }) - it('should default to not replacing all instances', function () { - expect(new Rule().replaceAll).equal(false) + t.test('should default to not replacing all instances', function (t) { + t.equal(new Rule().replaceAll, false) + t.end() }) - it('should default to not ignoring matching URLs', function () { - expect(new Rule().ignore).equal(false) + t.test('should default to not ignoring matching URLs', function (t) { + t.equal(new Rule().ignore, false) + t.end() }) - it('should silently pass through the input if applied', function () { - expect(new Rule().apply('sample/input')).equal('sample/input') + t.test('should silently pass through the input if applied', function (t) { + t.equal(new Rule().apply('sample/input'), 'sample/input') + t.end() }) }) - describe('when given a RegExp', function () { - it('should merge flags', function () { + t.test('when given a RegExp', function (t) { + t.autoend() + t.test('should merge flags', function (t) { const r = new Rule({ each_segment: false, eval_order: 0, @@ -233,14 +289,15 @@ describe('NormalizerRule', function () { }) const re = r.pattern - expect(re.ignoreCase).to.be.true - expect(re.multiline).to.be.true - expect(re.global).to.be.true + t.equal(re.ignoreCase, true) + t.equal(re.multiline, true) + t.equal(re.global, true) + t.end() }) - it('should not die on duplicated flags', function () { + t.test('should not die on duplicated flags', function (t) { let r = null - expect(function () { + t.doesNotThrow(function () { r = new Rule({ each_segment: false, eval_order: 0, @@ -250,12 +307,13 @@ describe('NormalizerRule', function () { ignore: false, replacement: 'y' }) - }).to.not.throw() + }) const re = r.pattern - expect(re.ignoreCase).to.be.true - expect(re.multiline).to.be.false - expect(re.global).to.be.true + t.equal(re.ignoreCase, true) + t.equal(re.multiline, false) + t.equal(re.global, true) + t.end() }) }) }) diff --git a/test/unit/metric/normalizer.test.js b/test/unit/metric/normalizer.test.js index 0df593cb22..8ac10fd010 100644 --- a/test/unit/metric/normalizer.test.js +++ b/test/unit/metric/normalizer.test.js @@ -4,59 +4,135 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') +const tap = require('tap') const Config = require('../../../lib/config') -const expect = chai.expect const Normalizer = require('../../../lib/metrics/normalizer') const stagingRules = require('./staging-rules') +function beforeEach(t) { + const config = { enforce_backstop: true } + t.context.normalizer = new Normalizer(config, 'URL') +} + +tap.test('MetricNormalizer', function (t) { + t.autoend() + t.test('normalize', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.test('should throw when instantiated without config', function (t) { + t.throws(function () { + // eslint-disable-next-line no-new + new Normalizer() + }) + t.end() + }) -describe('MetricNormalizer', function () { - let normalizer + t.test('should throw when instantiated without type', function (t) { + const config = { enforce_backstop: true } + t.throws(function () { + // eslint-disable-next-line no-new + new Normalizer(config) + }) + t.end() + }) - beforeEach(function () { - const config = { enforce_backstop: true } - normalizer = new Normalizer(config, 'URL') - }) + t.test('should normalize even without any rules set', function (t) { + const { normalizer } = t.context + t.equal(normalizer.normalize('/sample').value, 'NormalizedUri/*') + t.end() + }) - it('should throw when instantiated without config', function () { - expect(function () { - normalizer = new Normalizer() - }).throws() - }) + t.test('should normalize with an empty rule set', function (t) { + const { normalizer } = t.context + normalizer.load([]) - it('should throw when instantiated without type', function () { - const config = { enforce_backstop: true } - expect(function () { - normalizer = new Normalizer(config) - }).throws() - }) + t.equal(normalizer.normalize('/sample').value, 'NormalizedUri/*') + t.end() + }) - it('should normalize even without any rules set', function () { - expect(function () { - expect(normalizer.normalize('/sample')).to.have.property('value', 'NormalizedUri/*') - }).not.throws() - }) + t.test('should ignore a matching name', function (t) { + const { normalizer } = t.context + normalizer.load([ + { + each_segment: false, + eval_order: 0, + terminate_chain: true, + match_expression: '^/long_polling$', + replace_all: false, + ignore: true, + replacement: '*' + } + ]) - it('should normalize with an empty rule set', function () { - expect(function () { - normalizer.load([]) + t.equal(normalizer.normalize('/long_polling').ignore, true) + t.end() + }) - expect(normalizer.normalize('/sample')).to.have.property('value', 'NormalizedUri/*') - }).not.throws() + t.test('should apply rules by precedence', function (t) { + const { normalizer } = t.context + normalizer.load([ + { + each_segment: true, + eval_order: 1, + terminate_chain: false, + match_expression: 'mochi', + replace_all: false, + ignore: false, + replacement: 'millet' + }, + { + each_segment: false, + eval_order: 0, + terminate_chain: false, + match_expression: '/rice$', + replace_all: false, + ignore: false, + replacement: '/mochi' + } + ]) + + t.equal(normalizer.normalize('/rice/is/not/rice').value, 'NormalizedUri/rice/is/not/millet') + t.end() + }) + + t.test('should terminate when indicated by rule', function (t) { + const { normalizer } = t.context + normalizer.load([ + { + each_segment: true, + eval_order: 1, + terminate_chain: false, + match_expression: 'mochi', + replace_all: false, + ignore: false, + replacement: 'millet' + }, + { + each_segment: false, + eval_order: 0, + terminate_chain: true, + match_expression: '/rice$', + replace_all: false, + ignore: false, + replacement: '/mochi' + } + ]) + + t.equal(normalizer.normalize('/rice/is/not/rice').value, 'NormalizedUri/rice/is/not/mochi') + t.end() + }) }) - describe('with rules captured from the staging collector on 2012-08-29', function () { - beforeEach(function () { + t.test('with rules captured from the staging collector on 2012-08-29', function (t) { + t.autoend() + t.beforeEach(function (t) { + beforeEach(t) + const { normalizer } = t.context normalizer.load(stagingRules) }) - it('should eliminate duplicate rules as part of loading them', function () { + t.test('should eliminate duplicate rules as part of loading them', function (t) { + const { normalizer } = t.context const patternWithSlash = '^(.*)\\/[0-9][0-9a-f_,-]*\\.([0-9a-z][0-9a-z]*)$' const reduced = [ { @@ -97,32 +173,35 @@ describe('MetricNormalizer', function () { } ] - expect( + t.same( normalizer.rules.map((r) => { return r.toJSON() - }) - ).eql(reduced) + }), + reduced + ) + t.end() }) - it('should normalize a JPEGgy URL', function () { - expect(normalizer.normalize('/excessivity.jpeg')).to.have.property( - 'value', - 'NormalizedUri/*.jpeg' - ) + t.test('should normalize a JPEGgy URL', function (t) { + const { normalizer } = t.context + t.equal(normalizer.normalize('/excessivity.jpeg').value, 'NormalizedUri/*.jpeg') + t.end() }) - it('should normalize a JPGgy URL', function () { - expect(normalizer.normalize('/excessivity.jpg')).to.have.property( - 'value', - 'NormalizedUri/*.jpg' - ) + t.test('should normalize a JPGgy URL', function (t) { + const { normalizer } = t.context + t.equal(normalizer.normalize('/excessivity.jpg').value, 'NormalizedUri/*.jpg') + t.end() }) - it('should normalize a CSS URL', function () { - expect(normalizer.normalize('/style.css')).to.have.property('value', 'NormalizedUri/*.css') + t.test('should normalize a CSS URL', function (t) { + const { normalizer } = t.context + t.equal(normalizer.normalize('/style.css').value, 'NormalizedUri/*.css') + t.end() }) - it('should drop old rules when reloading', function () { + t.test('should drop old rules when reloading', function (t) { + const { normalizer } = t.context const newRule = { each_segment: false, eval_order: 0, @@ -143,115 +222,54 @@ describe('MetricNormalizer', function () { ignore: false, replacement: '$1' } - expect( + t.same( normalizer.rules.map((r) => { return r.toJSON() - }) - ).eql([expected]) + }), + [expected] + ) + t.end() }) }) - it('should ignore a matching name', function () { - normalizer.load([ - { - each_segment: false, - eval_order: 0, - terminate_chain: true, - match_expression: '^/long_polling$', - replace_all: false, - ignore: true, - replacement: '*' - } - ]) - - expect(normalizer.normalize('/long_polling')).to.have.property('ignore', true) - }) - - it('should apply rules by precedence', function () { - normalizer.load([ - { - each_segment: true, - eval_order: 1, - terminate_chain: false, - match_expression: 'mochi', - replace_all: false, - ignore: false, - replacement: 'millet' - }, - { - each_segment: false, - eval_order: 0, - terminate_chain: false, - match_expression: '/rice$', - replace_all: false, - ignore: false, - replacement: '/mochi' - } - ]) - - expect(normalizer.normalize('/rice/is/not/rice')).to.have.property( - 'value', - 'NormalizedUri/rice/is/not/millet' - ) - }) - - it('should terminate when indicated by rule', function () { - normalizer.load([ - { - each_segment: true, - eval_order: 1, - terminate_chain: false, - match_expression: 'mochi', - replace_all: false, - ignore: false, - replacement: 'millet' - }, - { - each_segment: false, - eval_order: 0, - terminate_chain: true, - match_expression: '/rice$', - replace_all: false, - ignore: false, - replacement: '/mochi' - } - ]) - - expect(normalizer.normalize('/rice/is/not/rice')).to.have.property( - 'value', - 'NormalizedUri/rice/is/not/mochi' - ) - }) - - describe('when calling addSimple', function () { - it("won't crash with no parameters", function () { - expect(function () { + t.test('when calling addSimple', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.test("won't crash with no parameters", function (t) { + const { normalizer } = t.context + t.doesNotThrow(function () { normalizer.addSimple() - }).not.throws() + }) + t.end() }) - it("won't crash when name isn't passed", function () { - expect(function () { + t.test("won't crash when name isn't passed", function (t) { + const { normalizer } = t.context + t.doesNotThrow(function () { normalizer.addSimple('^t') - }).not.throws() + }) + t.end() }) - it("will ignore matches when name isn't passed", function () { + t.test("will ignore matches when name isn't passed", function (t) { + const { normalizer } = t.context normalizer.addSimple('^t') - expect(normalizer.rules[0].ignore).equal(true) + t.equal(normalizer.rules[0].ignore, true) + t.end() }) - it('will create rename rules that work properly', function () { + t.test('will create rename rules that work properly', function (t) { + const { normalizer } = t.context normalizer.addSimple('^/t(.*)$', '/w$1') - expect(normalizer.normalize('/test')).to.have.property('value', 'NormalizedUri/west') + t.equal(normalizer.normalize('/test').value, 'NormalizedUri/west') + t.end() }) }) - describe('when loading from config', function () { - let config = null - - beforeEach(function () { - config = new Config({ + t.test('when loading from config', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.config = new Config({ rules: { name: [ { pattern: '^first$', name: 'first', precedence: 500 }, @@ -262,49 +280,33 @@ describe('MetricNormalizer', function () { } }) - normalizer = new Normalizer(config, 'URL') + t.context.normalizer = new Normalizer(t.context.config, 'URL') }) - afterEach(function () { - config = null - normalizer = null + t.afterEach(function (t) { + t.context.config = null + t.context.normalizer = null }) - describe('with feature flag reverse_naming_rules', function () { - describe('set to true', function () { - beforeEach(function () { - config.feature_flag = { reverse_naming_rules: true } - normalizer.loadFromConfig() - }) - - it('should respect precedence', function () { - expect(normalizer.rules[1]).to.have.property('replacement', 'third') - }) - - it('should have the rules in reverse order', function () { - expect(normalizer.rules[1]).to.have.property('replacement', 'third') - expect(normalizer.rules[2]).to.have.property('replacement', 'fourth') - expect(normalizer.rules[3]).to.have.property('replacement', 'second') - expect(normalizer.rules[4]).to.have.property('replacement', 'first') - }) - }) - - describe('set to false (default)', function () { - beforeEach(function () { - normalizer.loadFromConfig() - }) - - it('should respect precedence', function () { - expect(normalizer.rules[1]).to.have.property('replacement', 'third') - }) + t.test('with feature flag reverse_naming_rules set to true', function (t) { + const { config, normalizer } = t.context + config.feature_flag = { reverse_naming_rules: true } + normalizer.loadFromConfig() + t.equal(normalizer.rules[1].replacement, 'third') + t.equal(normalizer.rules[2].replacement, 'fourth') + t.equal(normalizer.rules[3].replacement, 'second') + t.equal(normalizer.rules[4].replacement, 'first') + t.end() + }) - it('should have the rules in forward order', function () { - expect(normalizer.rules[1]).to.have.property('replacement', 'third') - expect(normalizer.rules[2]).to.have.property('replacement', 'first') - expect(normalizer.rules[3]).to.have.property('replacement', 'second') - expect(normalizer.rules[4]).to.have.property('replacement', 'fourth') - }) - }) + t.test('with feature flag reverse_naming_rules set to false (default)', function (t) { + const { normalizer } = t.context + normalizer.loadFromConfig() + t.equal(normalizer.rules[1].replacement, 'third') + t.equal(normalizer.rules[2].replacement, 'first') + t.equal(normalizer.rules[3].replacement, 'second') + t.equal(normalizer.rules[4].replacement, 'fourth') + t.end() }) }) }) diff --git a/test/unit/metrics-mapper.test.js b/test/unit/metrics-mapper.test.js index 73a46ac21c..52f86dc38b 100644 --- a/test/unit/metrics-mapper.test.js +++ b/test/unit/metrics-mapper.test.js @@ -4,85 +4,92 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const MetricMapper = require('../../lib/metrics/mapper.js') -describe('MetricMapper', function () { - it("shouldn't throw if passed null", function () { - expect(function () { +tap.test('MetricMapper', function (t) { + t.test("shouldn't throw if passed null", function (t) { + t.doesNotThrow(function () { new MetricMapper().load(null) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if passed undefined", function () { - expect(function () { + t.test("shouldn't throw if passed undefined", function (t) { + t.doesNotThrow(function () { new MetricMapper().load(undefined) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if passed an empty list", function () { - expect(function () { + t.test("shouldn't throw if passed an empty list", function (t) { + t.doesNotThrow(function () { new MetricMapper().load([]) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if passed garbage input", function () { - expect(function () { + t.test("shouldn't throw if passed garbage input", function (t) { + t.doesNotThrow(function () { new MetricMapper().load({ name: 'garbage' }, 1001) - }).not.throws() + }) + t.end() }) - describe('when loading mappings at creation', function () { + t.test('when loading mappings at creation', function (t) { let mapper - before(function () { + t.before(function () { mapper = new MetricMapper([ [{ name: 'Test/RenameMe1' }, 1001], [{ name: 'Test/RenameMe2', scope: 'TEST' }, 1002] ]) }) - it('should have loaded all the mappings', function () { - expect(mapper.length).equal(2) + t.test('should have loaded all the mappings', function (t) { + t.equal(mapper.length, 2) + t.end() }) - it('should apply mappings', function () { - expect(mapper.map('Test/RenameMe1')).equal(1001) - expect(mapper.map('Test/RenameMe2', 'TEST')).equal(1002) + t.test('should apply mappings', function (t) { + t.equal(mapper.map('Test/RenameMe1'), 1001) + t.equal(mapper.map('Test/RenameMe2', 'TEST'), 1002) + t.end() }) - it('should turn non-mapped metrics into specs', function () { - expect(mapper.map('Test/Metric1')).deep.equal({ name: 'Test/Metric1' }) - expect(mapper.map('Test/Metric2', 'TEST')).deep.equal({ name: 'Test/Metric2', scope: 'TEST' }) + t.test('should turn non-mapped metrics into specs', function (t) { + t.same(mapper.map('Test/Metric1'), { name: 'Test/Metric1' }) + t.same(mapper.map('Test/Metric2', 'TEST'), { name: 'Test/Metric2', scope: 'TEST' }) + t.end() }) + t.end() }) - describe('when adding mappings after creation', function () { + t.test('when adding mappings after creation', function (t) { const mapper = new MetricMapper() - before(function () { + t.before(function () { mapper.load([[{ name: 'Test/RenameMe1' }, 1001]]) mapper.load([[{ name: 'Test/RenameMe2', scope: 'TEST' }, 1002]]) }) - it('should have loaded all the mappings', function () { - expect(mapper.length).equal(2) + t.test('should have loaded all the mappings', function (t) { + t.equal(mapper.length, 2) + t.end() }) - it('should apply mappings', function () { - expect(mapper.map('Test/RenameMe1')).equal(1001) - expect(mapper.map('Test/RenameMe2', 'TEST')).equal(1002) + t.test('should apply mappings', function (t) { + t.equal(mapper.map('Test/RenameMe1'), 1001) + t.equal(mapper.map('Test/RenameMe2', 'TEST'), 1002) + t.end() }) - it('should turn non-mapped metrics into specs', function () { - expect(mapper.map('Test/Metric1')).deep.equal({ name: 'Test/Metric1' }) - expect(mapper.map('Test/Metric2', 'TEST')).deep.equal({ name: 'Test/Metric2', scope: 'TEST' }) + t.test('should turn non-mapped metrics into specs', function (t) { + t.same(mapper.map('Test/Metric1'), { name: 'Test/Metric1' }) + t.same(mapper.map('Test/Metric2', 'TEST'), { name: 'Test/Metric2', scope: 'TEST' }) + t.end() }) + t.end() }) + t.end() }) diff --git a/test/unit/metrics-recorder/distributed-trace.test.js b/test/unit/metrics-recorder/distributed-trace.test.js index 2f138f9a7f..0e326c372d 100644 --- a/test/unit/metrics-recorder/distributed-trace.test.js +++ b/test/unit/metrics-recorder/distributed-trace.test.js @@ -5,12 +5,9 @@ 'use strict' -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - +const tap = require('tap') const helper = require('../../lib/agent_helper') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics +require('../../lib/metrics_helper') const recordDistributedTrace = require('../../../lib/metrics/recorders/distributed-trace') const Transaction = require('../../../lib/transaction') @@ -32,31 +29,34 @@ const record = (opts) => { recordDistributedTrace(tx, opts.type, duration, exclusive) } -describe('recordDistributedTrace', () => { - let agent - let tx - - beforeEach(() => { - agent = helper.loadMockedAgent({ - distributed_tracing: { - enabled: true - }, - cross_application_tracer: { enabled: true } - }) - // Set the DT required data after config runs, since they'll be cleared when - // not in serverless_mode - ;(agent.config.account_id = '1234'), - (agent.config.primary_application_id = '5678'), - (agent.config.trusted_account_key = '1234') - tx = new Transaction(agent) +function beforeEach(t) { + const agent = helper.loadMockedAgent({ + distributed_tracing: { + enabled: true + }, + cross_application_tracer: { enabled: true } }) + // Set the DT required data after config runs, since they'll be cleared when + // not in serverless_mode + ;(agent.config.account_id = '1234'), + (agent.config.primary_application_id = '5678'), + (agent.config.trusted_account_key = '1234') + t.context.tx = new Transaction(agent) + t.context.agent = agent +} - afterEach(() => { - helper.unloadAgent(agent) - }) +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} - describe('when a trace payload was received', () => { - it('records metrics with payload information', () => { +tap.test('recordDistributedTrace', (t) => { + t.autoend() + t.test('when a trace payload was received', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('records metrics with payload information', (t) => { + const { tx } = t.context const payload = tx._createDistributedTracePayload().text() tx.isDistributedTrace = null tx._acceptDistributedTracePayload(payload, 'HTTP') @@ -87,58 +87,63 @@ describe('recordDistributedTrace', () => { ] ] - assertMetrics(tx.metrics, result, true, true) + t.assertMetrics(tx.metrics, result, true, true) + t.end() }) - describe('and transaction errors exist', () => { - it('includes error-related metrics', () => { - const payload = tx._createDistributedTracePayload().text() - tx.isDistributedTrace = null - tx._acceptDistributedTracePayload(payload, 'HTTP') - - tx.exceptions.push('some error') - - record({ - tx, - duration: 55, - exclusive: 55, - type: 'Web' - }) - - const result = [ - [ - { name: 'DurationByCaller/App/1234/5678/HTTP/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'ErrorsByCaller/App/1234/5678/HTTP/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'TransportDuration/App/1234/5678/HTTP/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'DurationByCaller/App/1234/5678/HTTP/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'ErrorsByCaller/App/1234/5678/HTTP/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'TransportDuration/App/1234/5678/HTTP/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ] - ] + t.test('and transaction errors exist includes error-related metrics', (t) => { + const { tx } = t.context + const payload = tx._createDistributedTracePayload().text() + tx.isDistributedTrace = null + tx._acceptDistributedTracePayload(payload, 'HTTP') + + tx.exceptions.push('some error') - assertMetrics(tx.metrics, result, true, true) + record({ + tx, + duration: 55, + exclusive: 55, + type: 'Web' }) + + const result = [ + [ + { name: 'DurationByCaller/App/1234/5678/HTTP/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'ErrorsByCaller/App/1234/5678/HTTP/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'TransportDuration/App/1234/5678/HTTP/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'DurationByCaller/App/1234/5678/HTTP/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'ErrorsByCaller/App/1234/5678/HTTP/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'TransportDuration/App/1234/5678/HTTP/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ] + ] + + t.assertMetrics(tx.metrics, result, true, true) + t.end() }) }) - describe('when no trace payload was received', () => { - it('records metrics with Unknown payload information', () => { + t.test('when no trace payload was received', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('records metrics with Unknown payload information', (t) => { + const { tx } = t.context record({ tx, duration: 55, @@ -157,7 +162,8 @@ describe('recordDistributedTrace', () => { ] ] - assertMetrics(tx.metrics, result, true, true) + t.assertMetrics(tx.metrics, result, true, true) + t.end() }) }) }) diff --git a/test/unit/metrics-recorder/generic.test.js b/test/unit/metrics-recorder/generic.test.js index cda126a119..d863f3fdcd 100644 --- a/test/unit/metrics-recorder/generic.test.js +++ b/test/unit/metrics-recorder/generic.test.js @@ -4,13 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../../lib/agent_helper') const recordGeneric = require('../../../lib/metrics/recorders/generic') const Transaction = require('../../../lib/transaction') @@ -35,69 +29,71 @@ function record(options) { recordGeneric(segment, options.transaction.name) } -describe('recordGeneric', function () { - let agent - let trans +tap.test('recordGeneric', function (t) { + t.autoend() + t.beforeEach((t) => { + const agent = helper.loadMockedAgent() + t.context.trans = new Transaction(agent) + t.context.agent = agent + }) - beforeEach(function () { - agent = helper.loadMockedAgent() - trans = new Transaction(agent) + t.afterEach((t) => { + helper.unloadAgent(t.context.agent) }) - afterEach(function () { - helper.unloadAgent(agent) + t.test("when scoped is undefined it shouldn't crash on recording", function (t) { + const { trans } = t.context + const segment = makeSegment({ + transaction: trans, + duration: 0, + exclusive: 0 + }) + t.doesNotThrow(function () { + recordGeneric(segment, undefined) + }) + t.end() }) - describe('when scope is undefined', function () { - it("shouldn't crash on recording", function () { - const segment = makeSegment({ - transaction: trans, - duration: 0, - exclusive: 0 - }) - expect(function () { - recordGeneric(segment, undefined) - }).not.throws() + t.test('when scoped is undefined it should record no scoped metrics', function (t) { + const { trans } = t.context + const segment = makeSegment({ + transaction: trans, + duration: 5, + exclusive: 5 }) + recordGeneric(segment, undefined) - it('should record no scoped metrics', function () { - const segment = makeSegment({ - transaction: trans, - duration: 5, - exclusive: 5 - }) - recordGeneric(segment, undefined) + const result = [[{ name: 'placeholder' }, [1, 0.005, 0.005, 0.005, 0.005, 0.000025]]] - const result = [[{ name: 'placeholder' }, [1, 0.005, 0.005, 0.005, 0.005, 0.000025]]] + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() + }) - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) + t.test('with scope should record scoped metrics', function (t) { + const { trans } = t.context + record({ + transaction: trans, + url: '/test', + code: 200, + apdexT: 10, + duration: 30, + exclusive: 2 }) - }) - describe('with scope', function () { - it('should record scoped metrics', function () { - record({ - transaction: trans, - url: '/test', - code: 200, - apdexT: 10, - duration: 30, - exclusive: 2 - }) - - const result = [ - [{ name: 'placeholder' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], - [ - { name: 'placeholder', scope: 'WebTransaction/NormalizedUri/*' }, - [1, 0.03, 0.002, 0.03, 0.03, 0.0009] - ] + const result = [ + [{ name: 'placeholder' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], + [ + { name: 'placeholder', scope: 'WebTransaction/NormalizedUri/*' }, + [1, 0.03, 0.002, 0.03, 0.03, 0.0009] ] + ] - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) - }) + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() }) - it('should report exclusive time correctly', function () { + t.test('should report exclusive time correctly', function (t) { + const { trans } = t.context const root = trans.trace.root const parent = root.add('Test/Parent', recordGeneric) const child1 = parent.add('Test/Child/1', recordGeneric) @@ -115,6 +111,7 @@ describe('recordGeneric', function () { ] trans.end() - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() }) }) diff --git a/test/unit/metrics-recorder/http-external.test.js b/test/unit/metrics-recorder/http-external.test.js index 8fee41a9af..dc099a5cfc 100644 --- a/test/unit/metrics-recorder/http-external.test.js +++ b/test/unit/metrics-recorder/http-external.test.js @@ -4,13 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../../lib/agent_helper') const generateRecorder = require('../../../lib/metrics/recorders/http_external') const Transaction = require('../../../lib/transaction') @@ -39,79 +33,82 @@ function record(options) { recordExternal(segment, options.transaction.name) } -describe('recordExternal', function () { - let agent - let trans - - beforeEach(function () { - agent = helper.loadMockedAgent() - trans = new Transaction(agent) +tap.test('recordExternal', function (t) { + t.autoend() + t.beforeEach(function (t) { + const agent = helper.loadMockedAgent() + const trans = new Transaction(agent) trans.type = Transaction.TYPES.BG + t.context.agent = agent + t.context.trans = trans }) - afterEach(function () { - helper.unloadAgent(agent) + t.afterEach(function (t) { + helper.unloadAgent(t.context.agent) }) - describe('when scope is undefined', function () { - let segment - - beforeEach(function () { - segment = makeSegment({ - transaction: trans, - duration: 0, - exclusive: 0 - }) + t.test("when scoped is undefined it shouldn't crash on recording", function (t) { + const { trans } = t.context + const segment = makeSegment({ + transaction: trans, + duration: 0, + exclusive: 0 }) + t.doesNotThrow(function () { + recordExternal(segment, undefined) + }) + t.end() + }) - it("shouldn't crash on recording", function () { - expect(function () { - recordExternal(segment, undefined) - }).to.not.throw() + t.test('when scoped is undefined it should record no scoped metrics', function (t) { + const { trans } = t.context + const segment = makeSegment({ + transaction: trans, + duration: 0, + exclusive: 0 }) + recordExternal(segment, undefined) - it('should record no scoped metrics', function () { - recordExternal(segment, undefined) + const result = [ + [{ name: 'External/test.example.com/http' }, [1, 0, 0, 0, 0, 0]], + [{ name: 'External/allOther' }, [1, 0, 0, 0, 0, 0]], + [{ name: 'External/test.example.com/all' }, [1, 0, 0, 0, 0, 0]], + [{ name: 'External/all' }, [1, 0, 0, 0, 0, 0]] + ] - const result = [ - [{ name: 'External/test.example.com/http' }, [1, 0, 0, 0, 0, 0]], - [{ name: 'External/allOther' }, [1, 0, 0, 0, 0, 0]], - [{ name: 'External/test.example.com/all' }, [1, 0, 0, 0, 0, 0]], - [{ name: 'External/all' }, [1, 0, 0, 0, 0, 0]] - ] + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() + }) - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) + t.test('with scope should record scoped metrics', function (t) { + const { trans } = t.context + trans.type = Transaction.TYPES.WEB + record({ + transaction: trans, + url: '/test', + code: 200, + apdexT: 10, + duration: 30, + exclusive: 2 }) - }) - describe('with scope', function () { - it('should record scoped metrics', function () { - trans.type = Transaction.TYPES.WEB - record({ - transaction: trans, - url: '/test', - code: 200, - apdexT: 10, - duration: 30, - exclusive: 2 - }) - - const result = [ - [{ name: 'External/test.example.com/http' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], - [{ name: 'External/allWeb' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], - [{ name: 'External/test.example.com/all' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], - [{ name: 'External/all' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], - [ - { name: 'External/test.example.com/http', scope: 'WebTransaction/NormalizedUri/*' }, - [1, 0.03, 0.002, 0.03, 0.03, 0.0009] - ] + const result = [ + [{ name: 'External/test.example.com/http' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], + [{ name: 'External/allWeb' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], + [{ name: 'External/test.example.com/all' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], + [{ name: 'External/all' }, [1, 0.03, 0.002, 0.03, 0.03, 0.0009]], + [ + { name: 'External/test.example.com/http', scope: 'WebTransaction/NormalizedUri/*' }, + [1, 0.03, 0.002, 0.03, 0.03, 0.0009] ] + ] - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) - }) + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() }) - it('should report exclusive time correctly', function () { + t.test('should report exclusive time correctly', function (t) { + const { trans } = t.context const root = trans.trace.root const parent = root.add('/parent', recordExternal) const child1 = parent.add('/child1', generateRecorder('api.twitter.com', 'https')) @@ -134,6 +131,7 @@ describe('recordExternal', function () { ] trans.end() - expect(JSON.stringify(trans.metrics)).equal(JSON.stringify(result)) + t.equal(JSON.stringify(trans.metrics), JSON.stringify(result)) + t.end() }) }) diff --git a/test/unit/metrics-recorder/http.test.js b/test/unit/metrics-recorder/http.test.js index 7a71aa5a5b..4a2f8da734 100644 --- a/test/unit/metrics-recorder/http.test.js +++ b/test/unit/metrics-recorder/http.test.js @@ -4,15 +4,9 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../../lib/agent_helper') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics +require('../../lib/metrics_helper') const recordWeb = require('../../../lib/metrics/recorders/http') const Transaction = require('../../../lib/transaction') @@ -37,277 +31,309 @@ function record(options) { recordWeb(segment, options.transaction.name) } -describe('recordWeb', function () { - let agent - let trans - - beforeEach(function () { - agent = helper.instrumentMockedAgent() - trans = new Transaction(agent) - }) +function beforeEach(t) { + t.context.agent = helper.instrumentMockedAgent() + t.context.trans = new Transaction(t.context.agent) +} - afterEach(function () { - helper.unloadAgent(agent) - }) +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} - describe('when scope is undefined', function () { - let segment +tap.test('recordWeb', function (t) { + t.autoend() + t.test('when scope is undefined', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + + t.test("shouldn't crash on recording", function (t) { + const { trans } = t.context + t.doesNotThrow(function () { + const segment = makeSegment({ + transaction: trans, + duration: 0, + exclusive: 0 + }) + recordWeb(segment, undefined) + }) + t.end() + }) - beforeEach(function () { - segment = makeSegment({ + t.test('should record no metrics', function (t) { + const { trans } = t.context + const segment = makeSegment({ transaction: trans, duration: 0, exclusive: 0 }) - }) - - it("shouldn't crash on recording", function () { - expect(function () { - recordWeb(segment, undefined) - }).not.throws() - }) - - it('should record no metrics', function () { recordWeb(segment, undefined) - assertMetrics(trans.metrics, [], true, true) + t.assertMetrics(trans.metrics, [], true, true) + t.end() }) }) - describe('when recording web transactions', function () { - describe('with distributed tracing enabled', function () { - it('should record metrics from accepted payload information', function () { - agent.config.distributed_tracing.enabled = true - agent.config.cross_application_tracer.enabled = true - agent.config.account_id = '1234' - ;(agent.config.primary_application_id = '5677'), (agent.config.trusted_account_key = '1234') - - const payload = trans._createDistributedTracePayload().text() - trans.isDistributedTrace = null - trans._acceptDistributedTracePayload(payload, 'HTTP') - - record({ - transaction: trans, - apdexT: 0.06, - url: '/test', - code: 200, - duration: 55, - exclusive: 55 - }) + t.test('when recording web transactions with distributed tracing enabled', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should record metrics from accepted payload information', function (t) { + const { trans, agent } = t.context + agent.config.distributed_tracing.enabled = true + agent.config.cross_application_tracer.enabled = true + agent.config.account_id = '1234' + ;(agent.config.primary_application_id = '5677'), (agent.config.trusted_account_key = '1234') + + const payload = trans._createDistributedTracePayload().text() + trans.isDistributedTrace = null + trans._acceptDistributedTracePayload(payload, 'HTTP') - const result = [ - [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'DurationByCaller/App/1234/5677/HTTP/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'TransportDuration/App/1234/5677/HTTP/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'DurationByCaller/App/1234/5677/HTTP/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'TransportDuration/App/1234/5677/HTTP/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], - [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] - ] - - assertMetrics(trans.metrics, result, true, true) + record({ + transaction: trans, + apdexT: 0.06, + url: '/test', + code: 200, + duration: 55, + exclusive: 55 }) - it('should tag metrics with Unknown if no DT payload was received', function () { - agent.config.distributed_tracing.enabled = true - agent.config.cross_application_tracer.enabled = true - agent.config.account_id = '1234' - ;(agent.config.primary_application_id = '5677'), (agent.config.trusted_account_key = '1234') - - record({ - transaction: trans, - apdexT: 0.06, - url: '/test', - code: 200, - duration: 55, - exclusive: 55 - }) + const result = [ + [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'DurationByCaller/App/1234/5677/HTTP/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'TransportDuration/App/1234/5677/HTTP/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'DurationByCaller/App/1234/5677/HTTP/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'TransportDuration/App/1234/5677/HTTP/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], + [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] + ] - const result = [ - [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'DurationByCaller/Unknown/Unknown/Unknown/Unknown/all' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [ - { name: 'DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], - [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] - ] - - assertMetrics(trans.metrics, result, true, true) - }) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - describe('with normal requests', function () { - it('should infer a satisfying end-user experience', function () { - agent.config.distributed_tracing.enabled = false - - record({ - transaction: trans, - apdexT: 0.06, - url: '/test', - code: 200, - duration: 55, - exclusive: 55 - }) + t.test('should tag metrics with Unknown if no DT payload was received', function (t) { + const { trans, agent } = t.context + agent.config.distributed_tracing.enabled = true + agent.config.cross_application_tracer.enabled = true + agent.config.account_id = '1234' + ;(agent.config.primary_application_id = '5677'), (agent.config.trusted_account_key = '1234') - const result = [ - [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], - [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] - ] - assertMetrics(trans.metrics, result, true, true) + record({ + transaction: trans, + apdexT: 0.06, + url: '/test', + code: 200, + duration: 55, + exclusive: 55 }) - it('should infer a tolerable end-user experience', function () { - agent.config.distributed_tracing.enabled = false + const result = [ + [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'DurationByCaller/Unknown/Unknown/Unknown/Unknown/all' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [ + { name: 'DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], + [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] + ] - record({ - transaction: trans, - apdexT: 0.05, - url: '/test', - code: 200, - duration: 55, - exclusive: 100 - }) + t.assertMetrics(trans.metrics, result, true, true) + t.end() + }) + }) + + t.test('with normal requests', function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should infer a satisfying end-user experience', function (t) { + const { trans, agent } = t.context + agent.config.distributed_tracing.enabled = false - const result = [ - [{ name: 'WebTransaction' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.1, 0.1, 0.1, 0.1, 0.010000000000000002]], - [{ name: 'HttpDispatcher' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.1, 0.1, 0.1, 0.1, 0.010000000000000002] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [0, 1, 0, 0.05, 0.05, 0]], - [{ name: 'Apdex' }, [0, 1, 0, 0.05, 0.05, 0]] - ] - assertMetrics(trans.metrics, result, true, true) + record({ + transaction: trans, + apdexT: 0.06, + url: '/test', + code: 200, + duration: 55, + exclusive: 55 }) - it('should infer a frustrating end-user experience', function () { - agent.config.distributed_tracing.enabled = false + const result = [ + [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.06, 0.06, 0]], + [{ name: 'Apdex' }, [1, 0, 0, 0.06, 0.06, 0]] + ] + t.assertMetrics(trans.metrics, result, true, true) + t.end() + }) - record({ - transaction: trans, - apdexT: 0.01, - url: '/test', - code: 200, - duration: 55, - exclusive: 55 - }) + t.test('should infer a tolerable end-user experience', function (t) { + const { trans, agent } = t.context + agent.config.distributed_tracing.enabled = false - const result = [ - [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.055, 0.055, 0.055, 0.055, 0.003025] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [0, 0, 1, 0.01, 0.01, 0]], - [{ name: 'Apdex' }, [0, 0, 1, 0.01, 0.01, 0]] - ] - assertMetrics(trans.metrics, result, true, true) + record({ + transaction: trans, + apdexT: 0.05, + url: '/test', + code: 200, + duration: 55, + exclusive: 100 }) - it('should chop query strings delimited by ? from request URLs', function () { - record({ - transaction: trans, - url: '/test?test1=value1&test2&test3=50' - }) + const result = [ + [{ name: 'WebTransaction' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.1, 0.1, 0.1, 0.1, 0.010000000000000002]], + [{ name: 'HttpDispatcher' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.1, 0.055, 0.055, 0.003025]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.1, 0.1, 0.1, 0.1, 0.010000000000000002] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [0, 1, 0, 0.05, 0.05, 0]], + [{ name: 'Apdex' }, [0, 1, 0, 0.05, 0.05, 0]] + ] + t.assertMetrics(trans.metrics, result, true, true) + t.end() + }) + + t.test('should infer a frustrating end-user experience', function (t) { + const { trans, agent } = t.context + agent.config.distributed_tracing.enabled = false - expect(trans.url).equal('/test') + record({ + transaction: trans, + apdexT: 0.01, + url: '/test', + code: 200, + duration: 55, + exclusive: 55 }) - it('should chop query strings delimited by ; from request URLs', function () { - record({ - transaction: trans, - url: '/test;jsessionid=c83048283dd1328ac21aed8a8277d' - }) + const result = [ + [{ name: 'WebTransaction' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'HttpDispatcher' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.055, 0.055, 0.055, 0.055, 0.003025]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.055, 0.055, 0.055, 0.055, 0.003025] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [0, 0, 1, 0.01, 0.01, 0]], + [{ name: 'Apdex' }, [0, 0, 1, 0.01, 0.01, 0]] + ] + t.assertMetrics(trans.metrics, result, true, true) + t.end(0) + }) - expect(trans.url).equal('/test') + t.test('should chop query strings delimited by ? from request URLs', function (t) { + const { trans } = t.context + record({ + transaction: trans, + url: '/test?test1=value1&test2&test3=50' }) + + t.equal(trans.url, '/test') + t.end() }) - describe('with exceptional requests', function () { - it('should handle internal server errors', function () { - agent.config.distributed_tracing.enabled = false + t.test('should chop query strings delimited by ; from request URLs', function (t) { + const { trans } = t.context + record({ + transaction: trans, + url: '/test;jsessionid=c83048283dd1328ac21aed8a8277d' + }) - record({ - transaction: trans, - apdexT: 0.01, - url: '/test', - code: 500, - duration: 1, - exclusive: 1 - }) + t.equal(trans.url, '/test') + t.end() + }) + }) - const result = [ - [{ name: 'WebTransaction' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], - [{ name: 'WebTransactionTotalTime' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], - [{ name: 'HttpDispatcher' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], - [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], - [ - { name: 'WebTransactionTotalTime/NormalizedUri/*' }, - [1, 0.001, 0.001, 0.001, 0.001, 0.000001] - ], - [{ name: 'Apdex/NormalizedUri/*' }, [0, 0, 1, 0.01, 0.01, 0]], - [{ name: 'Apdex' }, [0, 0, 1, 0.01, 0.01, 0]] - ] - assertMetrics(trans.metrics, result, true, true) - }) + t.test('with exceptional requests should handle internal server errors', function (t) { + beforeEach(t) + afterEach(t) + const { agent, trans } = t.context + agent.config.distributed_tracing.enabled = false + + record({ + transaction: trans, + apdexT: 0.01, + url: '/test', + code: 500, + duration: 1, + exclusive: 1 }) + + const result = [ + [{ name: 'WebTransaction' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], + [{ name: 'WebTransactionTotalTime' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], + [{ name: 'HttpDispatcher' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], + [{ name: 'WebTransaction/NormalizedUri/*' }, [1, 0.001, 0.001, 0.001, 0.001, 0.000001]], + [ + { name: 'WebTransactionTotalTime/NormalizedUri/*' }, + [1, 0.001, 0.001, 0.001, 0.001, 0.000001] + ], + [{ name: 'Apdex/NormalizedUri/*' }, [0, 0, 1, 0.01, 0.01, 0]], + [{ name: 'Apdex' }, [0, 0, 1, 0.01, 0.01, 0]] + ] + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - describe("when testing a web request's apdex", function () { - it("shouldn't automatically mark ignored status codes as frustrating", function () { + t.test("when testing a web request's apdex", function (t) { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test("shouldn't automatically mark ignored status codes as frustrating", function (t) { + const { trans, agent } = t.context // FIXME: probably shouldn't do all this through side effects trans.statusCode = 404 trans._setApdex('Apdex/Uri/test', 30) const result = [[{ name: 'Apdex/Uri/test' }, [1, 0, 0, 0.1, 0.1, 0]]] - expect(agent.config.error_collector.ignore_status_codes).deep.equal([404]) - assertMetrics(trans.metrics, result, true, true) + t.same(agent.config.error_collector.ignore_status_codes, [404]) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - it('should handle ignored codes for the whole transaction', function () { + t.test('should handle ignored codes for the whole transaction', function (t) { + const { agent, trans } = t.context agent.config.distributed_tracing.enabled = false agent.config.error_collector.ignore_status_codes = [404, 500] @@ -332,18 +358,22 @@ describe('recordWeb', function () { [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.2, 0.2, 0]], [{ name: 'Apdex' }, [1, 0, 0, 0.2, 0.2, 0]] ] - assertMetrics(trans.metrics, result, true, true) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - it('should otherwise mark error status codes as frustrating', function () { + t.test('should otherwise mark error status codes as frustrating', function (t) { + const { trans } = t.context // FIXME: probably shouldn't do all this through side effects trans.statusCode = 503 trans._setApdex('Apdex/Uri/test', 30) const result = [[{ name: 'Apdex/Uri/test' }, [0, 0, 1, 0.1, 0.1, 0]]] - assertMetrics(trans.metrics, result, true, true) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - it('should handle non-ignored codes for the whole transaction', function () { + t.test('should handle non-ignored codes for the whole transaction', function (t) { + const { trans, agent } = t.context agent.config.distributed_tracing.enabled = false record({ transaction: trans, @@ -366,10 +396,12 @@ describe('recordWeb', function () { [{ name: 'Apdex/NormalizedUri/*' }, [0, 0, 1, 0.2, 0.2, 0]], [{ name: 'Apdex' }, [0, 0, 1, 0.2, 0.2, 0]] ] - assertMetrics(trans.metrics, result, true, true) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) - it('should reflect key transaction apdexT', function () { + t.test('should reflect key transaction apdexT', function (t) { + const { trans, agent } = t.context agent.config.web_transactions_apdex = { 'WebTransaction/WebFrameworkUri/TestJS//key/:id': 0.667, // just to make sure @@ -406,7 +438,8 @@ describe('recordWeb', function () { [{ name: 'Apdex/WebFrameworkUri/TestJS//key/:id' }, [0, 1, 0, 0.667, 0.667, 0]], [{ name: 'Apdex' }, [0, 1, 0, 0.2, 0.2, 0]] ] - assertMetrics(trans.metrics, result, true, true) + t.assertMetrics(trans.metrics, result, true, true) + t.end() }) }) }) diff --git a/test/unit/metrics-recorder/queue-time-http.test.js b/test/unit/metrics-recorder/queue-time-http.test.js index 820af94e95..42a1e2fd34 100644 --- a/test/unit/metrics-recorder/queue-time-http.test.js +++ b/test/unit/metrics-recorder/queue-time-http.test.js @@ -8,7 +8,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const tapAssertMetrics = require('../../lib/metrics_helper').tapAssertMetrics +require('../../lib/metrics_helper') const recordWeb = require('../../../lib/metrics/recorders/http') const Transaction = require('../../../lib/transaction') @@ -80,7 +80,7 @@ tap.test('when recording queueTime', (test) => { [{ name: 'Apdex' }, [1, 0, 0, 0.2, 0.2, 0]] ] - tapAssertMetrics(t, trans, result, true) + t.assertMetricValues(trans, result, true) t.end() }) @@ -116,7 +116,7 @@ tap.test('when recording queueTime', (test) => { [{ name: 'Apdex/NormalizedUri/*' }, [1, 0, 0, 0.2, 0.2, 0]], [{ name: 'Apdex' }, [1, 0, 0, 0.2, 0.2, 0]] ] - tapAssertMetrics(t, trans, result, true) + t.assertMetricValues(trans, result, true) t.end() }) diff --git a/test/unit/name-state.test.js b/test/unit/name-state.test.js index e1c128c547..e0837d1bdd 100644 --- a/test/unit/name-state.test.js +++ b/test/unit/name-state.test.js @@ -4,22 +4,19 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - +const tap = require('tap') const NameState = require('../../lib/transaction/name-state.js') -const expect = require('chai').expect -describe('NameState', function () { - it('should handle basic naming', function () { +tap.test('NameState', function (t) { + t.autoend() + t.test('should handle basic naming', function (t) { const state = new NameState('Nodejs', 'GET', '/', 'path1') state.appendPath('path2') - expect(state.getName()).to.equal('Nodejs/GET//path1/path2') + t.equal(state.getName(), 'Nodejs/GET//path1/path2') + t.end() }) - it('should handle piece-wise naming', function () { + t.test('should handle piece-wise naming', function (t) { const state = new NameState(null, null, null, null) state.setPrefix('Nodejs') state.setVerb('GET') @@ -27,70 +24,77 @@ describe('NameState', function () { state.appendPath('path1') state.appendPath('path2') state.appendPath('path3') - expect(state.getName()).to.equal('Nodejs/GET//path1/path2/path3') + t.equal(state.getName(), 'Nodejs/GET//path1/path2/path3') + t.end() }) - it('should handle missing components', function () { + t.test('should handle missing components', function (t) { let state = new NameState('Nodejs', null, null, 'path1') - expect(state.getName()).to.equal('Nodejs/path1') + t.equal(state.getName(), 'Nodejs/path1') state = new NameState('Nodejs', null, '/', 'path1') - expect(state.getName()).to.equal('Nodejs//path1') + t.equal(state.getName(), 'Nodejs//path1') state = new NameState(null, null, null, 'path1') - expect(state.getName()).to.equal('/path1') + t.equal(state.getName(), '/path1') state = new NameState('Nodejs', null, null, null) - expect(state.getName()).to.equal(null) + t.equal(state.getName(), null) + t.end() }) - it('should delete the name when reset', function () { + t.test('should delete the name when reset', function (t) { const state = new NameState('Nodejs', 'GET', '/', 'path1') - expect(state.getName()).to.equal('Nodejs/GET//path1') + t.equal(state.getName(), 'Nodejs/GET//path1') state.reset() - expect(state.getName()).to.equal(null) + t.equal(state.getName(), null) + t.end() }) - it('should handle regex paths', function () { + t.test('should handle regex paths', function (t) { const state = new NameState('Nodejs', 'GET', '/', []) state.appendPath(new RegExp('regex1')) state.appendPath('path1') state.appendPath(/regex2/) state.appendPath('path2') - expect(state.getPath()).to.equal('/regex1/path1/regex2/path2') - expect(state.getName()).to.equal('Nodejs/GET//regex1/path1/regex2/path2') + t.equal(state.getPath(), '/regex1/path1/regex2/path2') + t.equal(state.getName(), 'Nodejs/GET//regex1/path1/regex2/path2') + t.end() }) - it('should pick the current stack name over marked paths', function () { + t.test('should pick the current stack name over marked paths', function (t) { const state = new NameState('Nodejs', 'GET', '/') state.appendPath('path1') state.markPath() state.appendPath('path2') - expect(state.getPath()).to.equal('/path1/path2') - expect(state.getName()).to.equal('Nodejs/GET//path1/path2') + t.equal(state.getPath(), '/path1/path2') + t.equal(state.getName(), 'Nodejs/GET//path1/path2') + t.end() }) - it('should pick marked paths if the path stack is empty', function () { + t.test('should pick marked paths if the path stack is empty', function (t) { const state = new NameState('Nodejs', 'GET', '/') state.appendPath('path1') state.markPath() state.popPath() - expect(state.getPath()).to.equal('/path1') - expect(state.getName()).to.equal('Nodejs/GET//path1') + t.equal(state.getPath(), '/path1') + t.equal(state.getName(), 'Nodejs/GET//path1') + t.end() }) - it('should not report as empty if a path has been marked', function () { + t.test('should not report as empty if a path has been marked', function (t) { const state = new NameState('Nodejs', 'GET', '/') - expect(state.isEmpty()).to.be.true + t.equal(state.isEmpty(), true) state.appendPath('path1') state.markPath() state.popPath() - expect(state.isEmpty()).to.be.false + t.equal(state.isEmpty(), false) + t.end() }) }) diff --git a/test/unit/rum.test.js b/test/unit/rum.test.js index ecd63c806c..9c53c5f859 100644 --- a/test/unit/rum.test.js +++ b/test/unit/rum.test.js @@ -4,26 +4,16 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const assert = require('assert') +const tap = require('tap') const helper = require('../lib/agent_helper') const API = require('../../api') const hashes = require('../../lib/util/hashes') -chai.should() - -describe('the RUM API', function () { - let agent - let api - - beforeEach(function () { - agent = helper.loadMockedAgent({ +tap.test('the RUM API', function (t) { + t.autoend() + t.beforeEach(function (t) { + const agent = helper.loadMockedAgent({ license_key: 'license key here', browser_monitoring: { attributes: { @@ -37,165 +27,213 @@ describe('the RUM API', function () { agent.config.application_id = 12345 agent.config.browser_monitoring.browser_key = 1234 agent.config.browser_monitoring.js_agent_loader = 'function() {}' - api = new API(agent) + t.context.api = new API(agent) + t.context.agent = agent }) - afterEach(function () { - helper.unloadAgent(agent) + t.afterEach(function (t) { + helper.unloadAgent(t.context.agent) }) - it('should not generate header when disabled', function () { + t.test('should not generate header when disabled', function (t) { + const { agent, api } = t.context agent.config.browser_monitoring.enable = false - api.getBrowserTimingHeader().should.equal('') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) - it('should issue a warning outside a transaction by default', function () { - api.getBrowserTimingHeader().should.equal('') + t.test('should issue a warning outside a transaction by default', function (t) { + const { api } = t.context + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) - it('should issue a warning outside a transaction and allowTransactionlessInjection is false', function () { - api - .getBrowserTimingHeader({ allowTransactionlessInjection: false }) - .should.equal('') - }) + t.test( + 'should issue a warning outside a transaction and allowTransactionlessInjection is false', + function (t) { + const { api } = t.context + t.equal( + api.getBrowserTimingHeader({ allowTransactionlessInjection: false }), + '' + ) + t.end() + } + ) - it('should issue a warning if the transaction was ignored', function () { + t.test('should issue a warning if the transaction was ignored', function (t) { + const { agent, api } = t.context helper.runInTransaction(agent, function (tx) { tx.ignore = true - api.getBrowserTimingHeader().should.equal('') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should not generate header config is missing', function () { + t.test('should not generate header config is missing', function (t) { + const { agent, api } = t.context agent.config.browser_monitoring = undefined - api.getBrowserTimingHeader().should.equal('') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) - it('should issue a warning if transaction has no name', function () { + t.test('should issue a warning if transaction has no name', function (t) { + const { agent, api } = t.context helper.runInTransaction(agent, function () { - api.getBrowserTimingHeader().should.equal('') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should issue a warning without an application_id', function () { + t.test('should issue a warning without an application_id', function (t) { + const { agent, api } = t.context agent.config.application_id = undefined - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - api.getBrowserTimingHeader().should.equal('') + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should return the rum headers when in a named transaction', function () { - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - api.getBrowserTimingHeader().indexOf(' 5) + t.ok(api.getBrowserTimingHeader().split('\n').length > 5) + t.end() }) }) - it('should be compact when not debugging', function () { - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') + t.test('should be compact when not debugging', function (t) { + const { agent, api } = t.context + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') const l = api.getBrowserTimingHeader().split('\n').length - assert.equal(l, 1) + t.equal(l, 1) + t.end() }) }) - it('should return empty headers when missing browser_key', function () { + t.test('should return empty headers when missing browser_key', function (t) { + const { agent, api } = t.context agent.config.browser_monitoring.browser_key = undefined - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - api.getBrowserTimingHeader().should.equal('') + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should return empty headers when missing js_agent_loader', function () { + t.test('should return empty headers when missing js_agent_loader', function (t) { + const { agent, api } = t.context agent.config.browser_monitoring.js_agent_loader = '' - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - api.getBrowserTimingHeader().should.equal('') + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should be empty headers when loader is none', function () { + t.test('should be empty headers when loader is none', function (t) { + const { agent, api } = t.context agent.config.browser_monitoring.loader = 'none' - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - api.getBrowserTimingHeader().should.equal('') + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + t.equal(api.getBrowserTimingHeader(), '') + t.end() }) }) - it('should get browser agent script with wrapping tag', function () { - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') + t.test('should get browser agent script with wrapping tag', function (t) { + const { agent, api } = t.context + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') const timingHeader = api.getBrowserTimingHeader() - timingHeader - .startsWith( + t.ok( + timingHeader.startsWith( ``).should.equal(true) + ) + t.ok(timingHeader.endsWith(`}; function() {}`)) + t.end() }) }) - it('should get the browser agent script when outside a transaction and allowTransactionlessInjection is true', function () { - const timingHeader = api.getBrowserTimingHeader({ allowTransactionlessInjection: true }) - timingHeader - .startsWith( - ``).should.equal(true) - }) - - it('should get browser agent script with wrapping tag and add nonce attribute to script if passed in options', function () { - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - const timingHeader = api.getBrowserTimingHeader({ nonce: '12345' }) - timingHeader - .startsWith( - ``)) + t.end() + } + ) + + t.test( + 'should get browser agent script with wrapping tag and add nonce attribute to script if passed in options', + function (t) { + const { agent, api } = t.context + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + const timingHeader = api.getBrowserTimingHeader({ nonce: '12345' }) + t.ok( + timingHeader.startsWith( + ``).should.equal(true) - }) - }) - - it('should get browser agent script without wrapping tag if hasToRemoveScriptWrapper passed in options', function () { - helper.runInTransaction(agent, function (t) { - t.finalizeNameFromUri('hello') - const timingHeader = api.getBrowserTimingHeader({ hasToRemoveScriptWrapper: true }) - timingHeader - .startsWith( - 'window.NREUM||(NREUM={});NREUM.info = {"licenseKey":1234,"applicationID":12345,' + t.ok(timingHeader.endsWith(`}; function() {}`)) + t.end() + }) + } + ) + + t.test( + 'should get browser agent script without wrapping tag if hasToRemoveScriptWrapper passed in options', + function (t) { + const { agent, api } = t.context + helper.runInTransaction(agent, function (tx) { + tx.finalizeNameFromUri('hello') + const timingHeader = api.getBrowserTimingHeader({ hasToRemoveScriptWrapper: true }) + t.ok( + timingHeader.startsWith( + 'window.NREUM||(NREUM={});NREUM.info = {"licenseKey":1234,"applicationID":12345,' + ) ) - .should.equal(true) - timingHeader.endsWith(`}; function() {}`).should.equal(true) - }) - }) - - it('should add custom attributes', function () { - helper.runInTransaction(agent, function (t) { + t.ok(timingHeader.endsWith(`}; function() {}`)) + t.end() + }) + } + ) + + t.test('should add custom attributes', function (t) { + const { agent, api } = t.context + helper.runInTransaction(agent, function (tx) { api.addCustomAttribute('hello', 1) - t.finalizeNameFromUri('hello') + tx.finalizeNameFromUri('hello') const payload = /"atts":"(.*)"/.exec(api.getBrowserTimingHeader()) - payload.should.not.be.null + t.ok(payload) const deobf = hashes.deobfuscateNameUsingKey( payload[1], agent.config.license_key.substr(0, 13) ) - JSON.parse(deobf).u.hello.should.equal(1) + t.equal(JSON.parse(deobf).u.hello, 1) + t.end() }) }) }) diff --git a/test/unit/sampler.test.js b/test/unit/sampler.test.js index f9bfafde8b..e5a3dbe263 100644 --- a/test/unit/sampler.test.js +++ b/test/unit/sampler.test.js @@ -4,49 +4,44 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - +const tap = require('tap') const Agent = require('../../lib/agent') const configurator = require('../../lib/config') -const expect = require('chai').expect const sampler = require('../../lib/sampler') +const sinon = require('sinon') const NAMES = require('../../lib/metrics/names') -describe('environmental sampler', function () { - let agent = null +tap.test('environmental sampler', function (t) { + t.autoend() const numCpus = require('os').cpus().length - const oldCpuUsage = process.cpuUsage - const oldUptime = process.uptime - - beforeEach(function () { - agent = new Agent(configurator.initialize()) - process.cpuUsage = function () { - // process.cpuUsage return values in cpu microseconds (1^-6) - return { user: 1e6 * numCpus, system: 1e6 * numCpus } - } - process.uptime = function () { - // process.uptime returns values in seconds - return 1 - } + + t.beforeEach(function (t) { + const sandbox = sinon.createSandbox() + t.context.sandbox = sandbox + // process.cpuUsage return values in cpu microseconds (1^-6) + sandbox + .stub(process, 'cpuUsage') + .callsFake(() => ({ user: 1e6 * numCpus, system: 1e6 * numCpus })) + // process.uptime returns values in seconds + sandbox.stub(process, 'uptime').callsFake(() => 1) + t.context.agent = new Agent(configurator.initialize()) }) - afterEach(function () { + t.afterEach(function (t) { sampler.stop() - process.cpuUsage = oldCpuUsage - process.uptime = oldUptime + t.context.sandbox.restore() }) - it('should have the native-metrics package available', function () { - expect(function () { + t.test('should have the native-metrics package available', function (t) { + t.doesNotThrow(function () { require('@newrelic/native-metrics') - }).to.not.throw() + }) + t.end() }) - it('should still gather native metrics when bound and unbound', function (done) { + t.test('should still gather native metrics when bound and unbound', function (t) { + const { agent } = t.context sampler.start(agent) sampler.stop() sampler.start(agent) @@ -60,10 +55,10 @@ describe('environmental sampler', function () { sampler.sampleGc(agent, sampler.nativeMetrics)() const loop = agent.metrics.getOrCreateMetric(NAMES.LOOP.USAGE) - expect(loop.callCount).to.be.above(1) - expect(loop.max).to.be.above(0) - expect(loop.min).to.be.at.most(loop.max) - expect(loop.total).to.be.at.least(loop.max) + t.ok(loop.callCount > 1) + t.ok(loop.max > 0) + t.ok(loop.min <= loop.max) + t.ok(loop.total >= loop.max) // Find at least one typed GC metric. const type = [ @@ -73,93 +68,109 @@ describe('environmental sampler', function () { 'ProcessWeakCallbacks', 'All' ].find((t) => agent.metrics.getOrCreateMetric(NAMES.GC.PREFIX + t).callCount) - expect(type).to.exist + t.ok(type) const gc = agent.metrics.getOrCreateMetric(NAMES.GC.PREFIX + type) - expect(gc).property('callCount').to.be.at.least(1) - expect(gc).property('total').to.be.at.least(0.001) // At least 1 ms of GC + t.ok(gc.callCount >= 1) + t.ok(gc.total >= 0.001) // At least 1 ms of GC const pause = agent.metrics.getOrCreateMetric(NAMES.GC.PAUSE_TIME) - expect(pause).property('callCount').to.be.at.least(gc.callCount) - expect(pause).property('total').to.be.at.least(gc.total) - - done() + t.ok(pause.callCount >= gc.callCount) + t.ok(pause.total >= gc.total) + t.end() }) }) - it('should gather loop metrics', function (done) { + t.test('should gather loop metrics', function (t) { + const { agent } = t.context sampler.start(agent) sampler.nativeMetrics.getLoopMetrics() spinLoop(function runLoop() { sampler.sampleLoop(agent, sampler.nativeMetrics)() const stats = agent.metrics.getOrCreateMetric(NAMES.LOOP.USAGE) - expect(stats.callCount).to.be.above(1) - expect(stats.max).to.be.above(0) - expect(stats.min).to.be.at.most(stats.max) - expect(stats.total).to.be.at.least(stats.max) - done() + t.ok(stats.callCount > 1) + t.ok(stats.max > 0) + t.ok(stats.min <= stats.max) + t.ok(stats.total >= stats.max) + t.end() }) }) - it('should depend on Agent to provide the current metrics summary', function () { - expect(function () { + t.test('should depend on Agent to provide the current metrics summary', function (t) { + const { agent } = t.context + t.doesNotThrow(function () { sampler.start(agent) - }).to.not.throw() - expect(function () { + }) + t.doesNotThrow(function () { sampler.stop(agent) - }).to.not.throw() + }) + t.end() }) - it('should default to a state of stopped', function () { - expect(sampler.state).equal('stopped') + t.test('should default to a state of stopped', function (t) { + t.equal(sampler.state, 'stopped') + t.end() }) - it('should say it is running after start', function () { + t.test('should say it is running after start', function (t) { + const { agent } = t.context sampler.start(agent) - expect(sampler.state).equal('running') + t.equal(sampler.state, 'running') + t.end() }) - it('should say it is stopped after stop', function () { + t.test('should say it is stopped after stop', function (t) { + const { agent } = t.context sampler.start(agent) - expect(sampler.state).equal('running') + t.equal(sampler.state, 'running') sampler.stop(agent) - expect(sampler.state).equal('stopped') + t.equal(sampler.state, 'stopped') + t.end() }) - it('should gather CPU user utilization metric', function () { + t.test('should gather CPU user utilization metric', function (t) { + const { agent } = t.context sampler.sampleCpu(agent)() const stats = agent.metrics.getOrCreateMetric(NAMES.CPU.USER_UTILIZATION) - expect(stats.callCount).equal(1) - expect(stats.total).equal(1) + t.equal(stats.callCount, 1) + t.equal(stats.total, 1) + t.end() }) - it('should gather CPU system utilization metric', function () { + t.test('should gather CPU system utilization metric', function (t) { + const { agent } = t.context sampler.sampleCpu(agent)() const stats = agent.metrics.getOrCreateMetric(NAMES.CPU.SYSTEM_UTILIZATION) - expect(stats.callCount).equal(1) - expect(stats.total).equal(1) + t.equal(stats.callCount, 1) + t.equal(stats.total, 1) + t.end() }) - it('should gather CPU user time metric', function () { + t.test('should gather CPU user time metric', function (t) { + const { agent } = t.context sampler.sampleCpu(agent)() const stats = agent.metrics.getOrCreateMetric(NAMES.CPU.USER_TIME) - expect(stats.callCount).equal(1) - expect(stats.total).equal(numCpus) + t.equal(stats.callCount, 1) + t.equal(stats.total, numCpus) + t.end() }) - it('should gather CPU sytem time metric', function () { + t.test('should gather CPU sytem time metric', function (t) { + const { agent } = t.context sampler.sampleCpu(agent)() const stats = agent.metrics.getOrCreateMetric(NAMES.CPU.SYSTEM_TIME) - expect(stats.callCount).equal(1) - expect(stats.total).equal(numCpus) + t.equal(stats.callCount, 1) + t.equal(stats.total, numCpus) + t.end() }) - it('should gather GC metrics', function (done) { + t.test('should gather GC metrics', function (t) { + const { agent } = t.context sampler.start(agent) // Clear up the current state of the metrics. @@ -176,64 +187,69 @@ describe('environmental sampler', function () { 'ProcessWeakCallbacks', 'All' ].find((t) => agent.metrics.getOrCreateMetric(NAMES.GC.PREFIX + t).callCount) - expect(type).to.exist + t.ok(type) const gc = agent.metrics.getOrCreateMetric(NAMES.GC.PREFIX + type) - expect(gc).property('callCount').to.be.at.least(1) + t.ok(gc.callCount >= 1) // Assuming GC to take some amount of time. // With Node 12, the minimum for this work often seems to be // around 0.0008 on the servers. - expect(gc).property('total').to.be.at.least(0.0005) + t.ok(gc.total >= 0.0004) const pause = agent.metrics.getOrCreateMetric(NAMES.GC.PAUSE_TIME) - expect(pause).property('callCount').to.be.at.least(gc.callCount) - expect(pause).property('total').to.be.at.least(gc.total) - - done() + t.ok(pause.callCount >= gc.callCount) + t.ok(pause.total >= gc.total) + t.end() }) }) - it('should not gather GC metrics if disabled', function () { + t.test('should not gather GC metrics if disabled', function (t) { + const { agent } = t.context agent.config.plugins.native_metrics.enabled = false sampler.start(agent) - expect(sampler.nativeMetrics).to.be.null + t.not(sampler.nativeMetrics) + t.end() }) - it('should catch if process.cpuUsage throws an error', function () { - process.cpuUsage = function () { - throw new Error('ohhhhhh boyyyyyy') - } + t.test('should catch if process.cpuUsage throws an error', function (t) { + const { agent } = t.context + const err = new Error('ohhhhhh boyyyyyy') + process.cpuUsage.throws(err) sampler.sampleCpu(agent)() const stats = agent.metrics.getOrCreateMetric('CPU/User/Utilization') - expect(stats.callCount).equal(0) + t.equal(stats.callCount, 0) + t.end() }) - it('should collect all specified memory statistics', function () { + t.test('should collect all specified memory statistics', function (t) { + const { agent } = t.context sampler.sampleMemory(agent)() Object.keys(NAMES.MEMORY).forEach(function testStat(memoryStat) { const metricName = NAMES.MEMORY[memoryStat] const stats = agent.metrics.getOrCreateMetric(metricName) - expect(stats.callCount, `${metricName} callCount`).to.equal(1) - expect(stats.max, `${metricName} max`).to.be.above(1) + t.equal(stats.callCount, 1, `${metricName} callCount`) + t.ok(stats.max > 1, `${metricName} max`) }) + t.end() }) - it('should catch if process.memoryUsage throws an error', function () { - const oldProcessMem = process.memoryUsage - process.memoryUsage = function () { + t.test('should catch if process.memoryUsage throws an error', function (t) { + const { agent, sandbox } = t.context + sandbox.stub(process, 'memoryUsage').callsFake(() => { throw new Error('your computer is on fire') - } + }) sampler.sampleMemory(agent)() const stats = agent.metrics.getOrCreateMetric('Memory/Physical') - expect(stats.callCount).equal(0) - process.memoryUsage = oldProcessMem + t.equal(stats.callCount, 0) + t.end() }) - it('should have some rough idea of how deep the event queue is', function (done) { + t.test('should have some rough idea of how deep the event queue is', function (t) { + const { agent } = t.context sampler.checkEvents(agent)() /* sampler.checkEvents works by creating a timer and using @@ -248,18 +264,18 @@ describe('environmental sampler', function () { */ setTimeout(function () { const stats = agent.metrics.getOrCreateMetric('Events/wait') - expect(stats.callCount).equal(1) + t.equal(stats.callCount, 1) /* process.hrtime will notice the passage of time, but this * happens too fast to measure any meaningful latency in versions * of Node that don't have process.hrtime available, so just make * sure we're not getting back undefined or null here. */ - expect(stats.total).a('number') + t.ok(typeof stats.total === 'number') if (process.hrtime) { - expect(stats.total).above(0) + t.ok(stats.total > 0) } - done() + t.end() }, 0) }) }) diff --git a/test/unit/shim/message-shim.test.js b/test/unit/shim/message-shim.test.js index fa5439e82d..7776dcf192 100644 --- a/test/unit/shim/message-shim.test.js +++ b/test/unit/shim/message-shim.test.js @@ -11,9 +11,6 @@ const hashes = require('../../../lib/util/hashes') const helper = require('../../lib/agent_helper') const MessageShim = require('../../../lib/shim/message-shim') -tap.Test.prototype.addAssert('isNonWritable', 1, helper.isNonWritable) -tap.Test.prototype.addAssert('compareSegments', 2, helper.compareSegments) - tap.test('MessageShim', function (t) { t.autoend() let agent = null diff --git a/test/unit/shim/shim.test.js b/test/unit/shim/shim.test.js index 36fb604bd6..17e575ee9d 100644 --- a/test/unit/shim/shim.test.js +++ b/test/unit/shim/shim.test.js @@ -10,8 +10,6 @@ const { EventEmitter } = require('events') const helper = require('../../lib/agent_helper') const Shim = require('../../../lib/shim/shim') const symbols = require('../../../lib/symbols') -tap.Test.prototype.addAssert('isNonWritable', 1, helper.isNonWritable) -tap.Test.prototype.addAssert('compareSegments', 2, helper.compareSegments) tap.test('Shim', function (t) { t.autoend() diff --git a/test/unit/shim/transaction-shim.test.js b/test/unit/shim/transaction-shim.test.js index a8fb1abb0a..7a49a1f52b 100644 --- a/test/unit/shim/transaction-shim.test.js +++ b/test/unit/shim/transaction-shim.test.js @@ -11,8 +11,6 @@ const helper = require('../../lib/agent_helper') const TransactionShim = require('../../../lib/shim/transaction-shim') const notRunningStates = ['stopped', 'stopping', 'errored'] -tap.Test.prototype.addAssert('isNonWritable', 1, helper.isNonWritable) - /** * Creates CAT headers to be used in handleMqTracingHeaders * tests below diff --git a/test/unit/shimmer.test.js b/test/unit/shimmer.test.js index 7f3085837c..d3f8720d30 100644 --- a/test/unit/shimmer.test.js +++ b/test/unit/shimmer.test.js @@ -5,26 +5,12 @@ 'use strict' -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. const tap = require('tap') -tap.mochaGlobals() - const oldInstrumentations = require('../../lib/instrumentations') const insPath = require.resolve('../../lib/instrumentations') const proxyquire = require('proxyquire') const sinon = require('sinon') -require.cache[insPath].exports = wrappedInst -function wrappedInst() { - const ret = oldInstrumentations() - ret['../lib/broken_instrumentation_module'] = { - module: '../test/lib/broken_instrumentation_module' - } - return ret -} -const chai = require('chai') -const expect = chai.expect const helper = require('../lib/agent_helper') const logger = require('../../lib/logger').child({ component: 'TEST' }) const shimmer = require('../../lib/shimmer') @@ -37,146 +23,168 @@ const TEST_MODULE_RELATIVE_PATH = `../helpers/node_modules/${TEST_MODULE_PATH}` const TEST_MODULE = 'sinon' const TEST_PATH_WITHIN = `${TEST_MODULE}/lib/sinon/spy` -describe('shimmer', function () { - describe('custom instrumentation', function () { - describe('of relative modules', makeModuleTests(TEST_MODULE_PATH, TEST_MODULE_RELATIVE_PATH)) - describe('of modules', makeModuleTests(TEST_MODULE, TEST_MODULE)) - describe( - 'of modules, where instrumentation fails', - makeModuleTests(TEST_MODULE, TEST_MODULE, true) - ) - describe('of deep modules', makeModuleTests(TEST_PATH_WITHIN, TEST_PATH_WITHIN)) - }) - - function makeModuleTests(moduleName, relativePath, throwsError) { - return function moduleTests() { - let agent = null - let onRequireArgs = null - let counter = 0 - let instrumentedModule = null - let errorThrown = 0 - let expectedErr - - beforeEach(function () { - agent = helper.instrumentMockedAgent() - const instrumentationOpts = { - moduleName: moduleName, - onRequire: function (shim, module) { - instrumentedModule = module - ++counter - onRequireArgs = arguments - if (throwsError) { - expectedErr = 'This threw an error! Oh no!' - throw new Error(expectedErr) - } - }, - onError: function (err) { - if (err.message === expectedErr) { - errorThrown += 1 - } - } +function makeModuleTests({ moduleName, relativePath, throwsError }, t) { + t.autoend() + t.beforeEach(function (t) { + t.context.counter = 0 + t.context.errorThrown = 0 + t.context.agent = helper.instrumentMockedAgent() + const instrumentationOpts = { + moduleName: moduleName, + onRequire: function (shim, module) { + t.context.instrumentedModule = module + ++t.context.counter + t.context.onRequireArgs = arguments + if (throwsError) { + t.context.expectedErr = 'This threw an error! Oh no!' + throw new Error(t.context.expectedErr) } - shimmer.registerInstrumentation(instrumentationOpts) - }) + }, + onError: function (err) { + if (err.message === t.context.expectedErr) { + t.context.errorThrown += 1 + } + } + } + shimmer.registerInstrumentation(instrumentationOpts) + }) - afterEach(function () { - counter = 0 - errorThrown = 0 - onRequireArgs = null + t.afterEach(function (t) { + t.context.onRequireArgs = null - clearCachedModules([relativePath]) + clearCachedModules([relativePath]) - helper.unloadAgent(agent) - }) + helper.unloadAgent(t.context.agent) + }) - it('should be sent a shim and the loaded module', function () { - const mod = require(relativePath) - expect(onRequireArgs.length).to.equal(3) - expect(onRequireArgs[0]).to.be.an.instanceof(shims.Shim) - expect(onRequireArgs[1]).to.equal(mod) - expect(onRequireArgs[2]).to.equal(moduleName) - }) + t.test('should be sent a shim and the loaded module', function (t) { + const mod = require(relativePath) + const { onRequireArgs } = t.context + t.equal(onRequireArgs.length, 3) + t.ok(onRequireArgs[0] instanceof shims.Shim) + t.equal(onRequireArgs[1], mod) + t.equal(onRequireArgs[2], moduleName) + t.end() + }) - it('should construct a DatastoreShim if the type is "datastore"', function () { - shimmer.registeredInstrumentations[moduleName][0].type = 'datastore' - require(relativePath) - expect(onRequireArgs[0]).to.be.an.instanceof(shims.DatastoreShim) - }) + t.test('should construct a DatastoreShim if the type is "datastore"', function (t) { + shimmer.registeredInstrumentations[moduleName][0].type = 'datastore' + require(relativePath) + const { onRequireArgs } = t.context + t.ok(onRequireArgs[0] instanceof shims.DatastoreShim) + t.end() + }) - it('should receive the correct module (' + moduleName + ')', function () { - const mod = require(relativePath) - expect(mod).to.equal(instrumentedModule) - }) + t.test('should receive the correct module (' + moduleName + ')', function (t) { + const mod = require(relativePath) + t.equal(mod, t.context.instrumentedModule) + t.end() + }) - it('should only run the instrumentation once', function () { - expect(counter).to.equal(0) - require(relativePath) - expect(counter).to.equal(1) - require(relativePath) - require(relativePath) - require(relativePath) - require(relativePath) - expect(counter).to.equal(1) - }) + t.test('should only run the instrumentation once', function (t) { + t.equal(t.context.counter, 0) + require(relativePath) + t.equal(t.context.counter, 1) + require(relativePath) + require(relativePath) + require(relativePath) + require(relativePath) + t.equal(t.context.counter, 1) + t.end() + }) - it('should have some NR properties after instrumented', () => { - const mod = require(relativePath) - const nrKeys = getNRSymbols(mod) + t.test('should have some NR properties after instrumented', (t) => { + const mod = require(relativePath) + const nrKeys = getNRSymbols(mod) - const message = `Expected to have Symbol(shim) but found ${nrKeys}.` - expect(nrKeys, message).to.include.members(['Symbol(shim)']) - }) + const message = `Expected to have Symbol(shim) but found ${nrKeys}.` + t.ok(nrKeys.includes('Symbol(shim)'), message) + t.end() + }) - it('should clean up NR added properties', () => { - const nrKeys = getNRSymbols(instrumentedModule) + t.test('should clean up NR added properties', (t) => { + const mod = require(relativePath) + shimmer.unwrapAll() + const nrKeys = getNRSymbols(mod) - const message = `Expected keys to be equal but found: ${JSON.stringify(nrKeys)}` - expect(nrKeys.length, message).to.equal(0) - }) + const message = `Expected keys to be equal but found: ${JSON.stringify(nrKeys)}` + t.equal(nrKeys.length, 0, message) + t.end() + }) - if (throwsError) { - it('should send error to onError handler', () => { - require(relativePath) - expect(errorThrown).to.equal(1) - }) - } - } + if (throwsError) { + t.test('should send error to onError handler', (t) => { + require(relativePath) + t.equal(t.context.errorThrown, 1) + t.end() + }) } +} - describe('wrapping exports', function () { - let agent = null - let original = null - let wrapper = null +tap.test('shimmer', function (t) { + t.autoend() + t.test('custom instrumentation', function (t) { + t.autoend() + t.test( + 'of relative modules', + makeModuleTests.bind(this, { + moduleName: TEST_MODULE_PATH, + relativePath: TEST_MODULE_RELATIVE_PATH + }) + ) + t.test( + 'of modules', + makeModuleTests.bind(this, { moduleName: TEST_MODULE, relativePath: TEST_MODULE }) + ) + t.test( + 'of modules, where instrumentation fails', + makeModuleTests.bind(this, { + moduleName: TEST_MODULE, + relativePath: TEST_MODULE, + throwsError: true + }) + ) + t.test( + 'of deep modules', + makeModuleTests.bind(this, { moduleName: TEST_PATH_WITHIN, relativePath: TEST_PATH_WITHIN }) + ) + }) - beforeEach(function () { - agent = helper.instrumentMockedAgent() + t.test('wrapping exports', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.agent = helper.instrumentMockedAgent() shimmer.registerInstrumentation({ moduleName: TEST_MODULE_PATH, onRequire: function (shim, nodule) { - original = nodule - wrapper = {} + const original = nodule + const wrapper = {} shim.wrapExport(original, function () { return wrapper }) + t.context.wrapper = wrapper + t.context.original = original } }) }) - afterEach(function () { - helper.unloadAgent(agent) + t.afterEach(function (t) { + helper.unloadAgent(t.context.agent) clearCachedModules([TEST_MODULE_RELATIVE_PATH]) - original = null - wrapper = null }) - it('should replace the return value from require', function () { + t.test('should replace the return value from require', function (t) { const obj = require(TEST_MODULE_RELATIVE_PATH) - expect(obj).to.equal(wrapper).and.not.equal(original) + const { wrapper, original } = t.context + t.equal(obj, wrapper) + t.not(obj, original) + t.end() }) }) - describe('the instrumentation injector', function () { + t.test('the instrumentation injector', function (t) { + t.autoend() const nodule = { c: 2, ham: 'ham', @@ -194,14 +202,15 @@ describe('shimmer', function () { } } - it('should not wrap anything without enough information', () => { + t.test('should not wrap anything without enough information', (t) => { shimmer.wrapMethod(nodule, 'nodule') - expect(shimmer.isWrapped(nodule.doubler)).equal(false) + t.equal(shimmer.isWrapped(nodule.doubler), false) shimmer.wrapMethod(nodule, 'nodule', 'doubler') - expect(shimmer.isWrapped(nodule.doubler)).equal(false) + t.equal(shimmer.isWrapped(nodule.doubler), false) + t.end() }) - it('should wrap a method', function () { + t.test('should wrap a method', function (t) { let doubled = 0 let before = false let after = false @@ -214,19 +223,20 @@ describe('shimmer', function () { } }) - expect(shimmer.isWrapped(nodule.doubler)).equal(true) - expect(nodule.doubler[symbols.unwrap]).a('function') + t.equal(shimmer.isWrapped(nodule.doubler), true) + t.ok(typeof nodule.doubler[symbols.unwrap] === 'function') nodule.doubler(7, function (z) { doubled = z }) - expect(doubled).equal(16) - expect(before).equal(true) - expect(after).equal(true) + t.equal(doubled, 16) + t.equal(before, true) + t.equal(after, true) + t.end() }) - it('should preserve properties on wrapped methods', () => { + t.test('should preserve properties on wrapped methods', (t) => { let quadrupled = 0 let before = false let after = false @@ -241,99 +251,123 @@ describe('shimmer', function () { } }) - expect(nodule.quadrupler[symbols.unwrap]).a('function') - expect(nodule.quadrupler.test).to.be.a('function') + t.ok(typeof nodule.quadrupler[symbols.unwrap] === 'function') + t.ok(typeof nodule.quadrupler.test === 'function') nodule.quadrupler(7, function (z) { quadrupled = z }) - expect(quadrupled).equal(30) - expect(before).equal(true) - expect(after).equal(true) + t.equal(quadrupled, 30) + t.equal(before, true) + t.equal(after, true) + t.end() }) - it('should not error out on external instrumentations that fail', function () { - expect(function () { + t.test('should not error out on external instrumentations that fail', function (t) { + t.teardown(() => { + require.cache[insPath].exports = oldInstrumentations + }) + + require.cache[insPath].exports = wrappedInst + function wrappedInst() { + const ret = oldInstrumentations() + ret['../lib/broken_instrumentation_module'] = { + module: '../test/lib/broken_instrumentation_module' + } + return ret + } + t.doesNotThrow(function () { require('../lib/broken_instrumentation_module') - }).not.throws() - require.cache[insPath].exports = oldInstrumentations + }) + t.end() }) - describe('with accessor replacement', function () { - let simple + t.test('with accessor replacement', function (t) { + t.autoend() - beforeEach(function () { - simple = { target: true } + t.beforeEach(function (t) { + t.context.simple = { target: true } }) - it("shouldn't throw if called with no params", function () { - expect(function () { + t.test("shouldn't throw if called with no params", function (t) { + t.doesNotThrow(function () { shimmer.wrapDeprecated() - }).not.throws() + }) + t.end() }) - it("shouldn't throw if called with only the original object", function () { - expect(function () { + t.test("shouldn't throw if called with only the original object", function (t) { + const { simple } = t.context + t.doesNotThrow(function () { shimmer.wrapDeprecated(simple) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if property to be replaced is omitted", function () { - expect(function () { + t.test("shouldn't throw if property to be replaced is omitted", function (t) { + const { simple } = t.context + t.doesNotThrow(function () { shimmer.wrapDeprecated(simple, 'nodule', null, { get: function () {}, set: function () {} }) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if getter is omitted", function () { - expect(function () { + t.test("shouldn't throw if getter is omitted", function (t) { + const { simple } = t.context + t.doesNotThrow(function () { shimmer.wrapDeprecated(simple, 'nodule', 'target', { set: function () {} }) - }).not.throws() + }) + t.end() }) - it("shouldn't throw if setter is omitted", function () { - expect(function () { + t.test("shouldn't throw if setter is omitted", function (t) { + const { simple } = t.context + t.doesNotThrow(function () { shimmer.wrapDeprecated(simple, 'nodule', 'target', { get: function () {} }) - }).not.throws() + }) + t.end() }) - it('should replace a property with an accessor', function (done) { + t.test('should replace a property with an accessor', function (t) { + const { simple } = t.context shimmer.debug = true // test internal debug code const original = shimmer.wrapDeprecated(simple, 'nodule', 'target', { get: function () { // test will only complete if this is called - done() return false } }) - expect(original).equal(true) + t.equal(original, true) - expect(simple.target).equal(false) + t.equal(simple.target, false) // internal debug code should unwrap - expect(shimmer.unwrapAll).not.throws() + t.doesNotThrow(shimmer.unwrapAll) + t.end() }) - it('should invoke the setter when the accessor is used', function (done) { + t.test('should invoke the setter when the accessor is used', function (t) { + const { simple } = t.context const test = 'ham' const original = shimmer.wrapDeprecated(simple, 'nodule', 'target', { get: function () { return test }, set: function (value) { - expect(value).equal('eggs') - done() + t.equal(value, 'eggs') + t.end() } }) - expect(original).equal(true) - expect(simple.target).equal('ham') + t.equal(original, true) + t.equal(simple.target, 'ham') simple.target = 'eggs' }) }) - it('should wrap, then unwrap a method', function () { + t.test('should wrap, then unwrap a method', function (t) { let tripled = 0 let before = false let after = false @@ -350,9 +384,9 @@ describe('shimmer', function () { tripled = z }) - expect(tripled).equal(23) - expect(before).equal(true) - expect(after).equal(true) + t.equal(tripled, 23) + t.equal(before, true) + t.equal(after, true) before = false after = false @@ -363,12 +397,13 @@ describe('shimmer', function () { tripled = j }) - expect(tripled).equal(29) - expect(before).equal(false) - expect(after).equal(false) + t.equal(tripled, 29) + t.equal(before, false) + t.equal(after, false) + t.end() }) - it("shouldn't break anything when an NR-wrapped method is wrapped again", function () { + t.test("shouldn't break anything when an NR-wrapped method is wrapped again", function (t) { let hamceptacle = '' let before = false let after = false @@ -393,25 +428,27 @@ describe('shimmer', function () { hamceptacle = k }) - expect(hamceptacle).equal('hamBurt') - expect(before).equal(true) - expect(after).equal(true) - expect(hammed).equal(true) + t.equal(hamceptacle, 'hamBurt') + t.equal(before, true) + t.equal(after, true) + t.equal(hammed, true) + t.end() }) - describe('with full instrumentation running', function () { - let agent + t.test('with full instrumentation running', function (t) { + t.autoend() - beforeEach(function () { - agent = helper.loadMockedAgent() + t.beforeEach(function (t) { + t.context.agent = helper.loadMockedAgent() }) - afterEach(function () { - helper.unloadAgent(agent) + t.afterEach(function (t) { + helper.unloadAgent(t.context.agent) }) - it('should push transactions through process.nextTick', function (done) { - expect(agent.getTransaction()).equal(null) + t.test('should push transactions through process.nextTick', function (t) { + const { agent } = t.context + t.equal(agent.getTransaction(), null) const synchronizer = new EventEmitter() const transactions = [] @@ -426,7 +463,7 @@ describe('shimmer', function () { process.nextTick( agent.tracer.bindFunction(function bindFunctionCb() { const lookup = agent.getTransaction() - expect(lookup).equal(current) + t.equal(lookup, current) synchronizer.emit('inner', lookup, i) }) @@ -438,13 +475,13 @@ describe('shimmer', function () { let doneCount = 0 synchronizer.on('inner', function (trans, j) { doneCount += 1 - expect(trans).equal(transactions[j]) - expect(trans.id).equal(ids[j]) + t.equal(trans, transactions[j]) + t.equal(trans.id, ids[j]) trans.end() if (doneCount === 10) { - return done() + t.end() } }) @@ -453,8 +490,9 @@ describe('shimmer', function () { } }) - it('should push transactions through setTimeout', function (done) { - expect(agent.getTransaction()).equal(null) + t.test('should push transactions through setTimeout', function (t) { + const { agent } = t.context + t.equal(agent.getTransaction(), null) const synchronizer = new EventEmitter() const transactions = [] @@ -469,7 +507,7 @@ describe('shimmer', function () { setTimeout( agent.tracer.bindFunction(function bindFunctionCb() { const lookup = agent.getTransaction() - expect(lookup).equal(current) + t.equal(lookup, current) synchronizer.emit('inner', lookup, i) }), @@ -482,13 +520,13 @@ describe('shimmer', function () { let doneCount = 0 synchronizer.on('inner', function (trans, j) { doneCount += 1 - expect(trans).equal(transactions[j]) - expect(trans.id).equal(ids[j]) + t.equal(trans, transactions[j]) + t.equal(trans.id, ids[j]) trans.end() if (doneCount === 10) { - return done() + t.end() } }) @@ -499,8 +537,9 @@ describe('shimmer', function () { } }) - it('should push transactions through EventEmitters', function (done) { - expect(agent.getTransaction()).equal(null) + t.test('should push transactions through EventEmitters', function (t) { + const { agent } = t.context + t.equal(agent.getTransaction(), null) const eventer = new EventEmitter() const transactions = [] @@ -519,8 +558,8 @@ describe('shimmer', function () { name, agent.tracer.bindFunction(function bindFunctionCb() { const lookup = agent.getTransaction() - expect(lookup).equal(current) - expect(lookup.id).equal(id) + t.equal(lookup, current) + t.equal(lookup.id, id) eventer.emit('inner', lookup, j) }) @@ -534,13 +573,13 @@ describe('shimmer', function () { let doneCount = 0 eventer.on('inner', function (trans, j) { doneCount += 1 - expect(trans).equal(transactions[j]) - expect(trans.id).equal(ids[j]) + t.equal(trans, transactions[j]) + t.equal(trans.id, ids[j]) trans.end() if (doneCount === 10) { - return done() + t.end() } }) @@ -549,8 +588,9 @@ describe('shimmer', function () { } }) - it('should handle whatever ridiculous nonsense you throw at it', function (done) { - expect(agent.getTransaction()).equal(null) + t.test('should handle whatever ridiculous nonsense you throw at it', function (t) { + const { agent } = t.context + t.equal(agent.getTransaction(), null) const synchronizer = new EventEmitter() const eventer = new EventEmitter() @@ -568,9 +608,9 @@ describe('shimmer', function () { passed ? passed.id : 'missing' ) - expect(lookup).equal(passed) - expect(lookup).equal(transactions[i]) - expect(lookup.id).equal(ids[i]) + t.equal(lookup, passed) + t.equal(lookup, transactions[i]) + t.equal(lookup.id, ids[i]) } eventer.on('rntest', function (trans, j) { @@ -609,13 +649,13 @@ describe('shimmer', function () { synchronizer.on('inner', function (trans, j) { verify(j, 'synchronizer', trans) doneCount += 1 - expect(trans).equal(transactions[j]) - expect(trans.id).equal(ids[j]) + t.equal(trans, transactions[j]) + t.equal(trans.id, ids[j]) trans.end() if (doneCount === 10) { - return done() + t.end() } }) diff --git a/test/unit/spans/span-event.test.js b/test/unit/spans/span-event.test.js index afdb945a09..7a6086aea1 100644 --- a/test/unit/spans/span-event.test.js +++ b/test/unit/spans/span-event.test.js @@ -7,7 +7,6 @@ const tap = require('tap') const DatastoreShim = require('../../../lib/shim/datastore-shim') -const expect = require('chai').expect const helper = require('../../lib/agent_helper') const https = require('https') const SpanEvent = require('../../../lib/spans/span-event') @@ -90,7 +89,7 @@ tap.test('fromSegment()', (t) => { t.equal(span.intrinsics.name, 'timers.setTimeout') t.equal(span.intrinsics.timestamp, segment.timer.start) - expect(span.intrinsics).to.have.property('duration').within(0.03, 0.3) + t.ok(span.intrinsics.duration >= 0.03 && span.intrinsics.duration <= 0.3) // Generic should not have 'span.kind' or 'component' t.equal(span.intrinsics['span.kind'], null) @@ -154,7 +153,7 @@ tap.test('fromSegment()', (t) => { t.equal(span.intrinsics.name, 'External/example.com/') t.equal(span.intrinsics.timestamp, segment.timer.start) - expect(span.intrinsics).to.have.property('duration').within(0.01, 2) + t.ok(span.intrinsics.duration >= 0.01 && span.intrinsics.duration <= 2) // Should have type-specific intrinsics t.equal(span.intrinsics.component, 'http') @@ -240,7 +239,7 @@ tap.test('fromSegment()', (t) => { t.equal(span.intrinsics.name, 'Datastore/statement/TestStore/test/test') t.equal(span.intrinsics.timestamp, segment.timer.start) - expect(span.intrinsics).to.have.property('duration').within(0.03, 0.7) + t.ok(span.intrinsics.duration >= 0.03 && span.intrinsics.duration <= 0.7) // Should have (most) type-specific intrinsics t.equal(span.intrinsics.component, 'TestStore') diff --git a/test/unit/stats.test.js b/test/unit/stats.test.js index 79c0348b8c..27636c0822 100644 --- a/test/unit/stats.test.js +++ b/test/unit/stats.test.js @@ -4,32 +4,29 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const Stats = require('../../lib/stats') -describe('Stats', function () { - let statistics +function verifyStats(actualStats, expectedStats) { + this.equal(actualStats.callCount, expectedStats.callCount) + this.equal(actualStats.total, expectedStats.totalTime) + this.equal(actualStats.totalExclusive, expectedStats.totalExclusive) + this.equal(actualStats.min, expectedStats.min) + this.equal(actualStats.max, expectedStats.max) + this.equal(actualStats.sumOfSquares, expectedStats.sumOfSquares) +} + +tap.Test.prototype.addAssert('verifyStats', 2, verifyStats) - function verifyStats(actualStats, expectedStats) { - expect(actualStats.callCount).equal(expectedStats.callCount) - expect(actualStats.total).equal(expectedStats.totalTime) - expect(actualStats.totalExclusive).equal(expectedStats.totalExclusive) - expect(actualStats.min).equal(expectedStats.min) - expect(actualStats.max).equal(expectedStats.max) - expect(actualStats.sumOfSquares).equal(expectedStats.sumOfSquares) - } +tap.test('Stats', function (t) { + t.autoend() - beforeEach(function () { - statistics = new Stats() + t.beforeEach(function (t) { + t.context.statistics = new Stats() }) - it('should correctly summarize a sample set of statistics', function () { + t.test('should correctly summarize a sample set of statistics', function (t) { + const { statistics } = t.context const expectedStats = { callCount: 3, totalTime: 0.306, @@ -43,10 +40,12 @@ describe('Stats', function () { statistics.recordValueInMillis(123, 34) statistics.recordValueInMillis(123, 34) - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) + t.end() }) - it('should correctly summarize another simple set of statistics', function () { + t.test('should correctly summarize another simple set of statistics', function (t) { + const { statistics } = t.context const expectedStats = { callCount: 2, totalTime: 0.24, @@ -59,11 +58,14 @@ describe('Stats', function () { statistics.recordValueInMillis(120, 0) statistics.recordValueInMillis(120, 0) - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) + t.end() }) - describe('when incrementing the call count', function () { - it('should increment by 1 by default', function () { + t.test('incrementCallCount', function (t) { + t.autoend() + t.test('should increment by 1 by default', function (t) { + const { statistics } = t.context const expectedStats = { callCount: 1, totalTime: 0, @@ -74,10 +76,12 @@ describe('Stats', function () { } statistics.incrementCallCount() - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) + t.end() }) - it('should increment by the provided value', function () { + t.test('should increment by the provided value', function (t) { + const { statistics } = t.context const expectedStats = { callCount: 23, totalTime: 0, @@ -88,10 +92,12 @@ describe('Stats', function () { } statistics.incrementCallCount(23) - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) + t.end() }) - it("shouldn't increment when the provided value is 0", function () { + t.test("shouldn't increment when the provided value is 0", function (t) { + const { statistics } = t.context const expectedStats = { callCount: 0, totalTime: 0, @@ -102,11 +108,13 @@ describe('Stats', function () { } statistics.incrementCallCount(0) - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) + t.end() }) }) - it('should correctly merge summaries', function () { + t.test('should correctly merge summaries', function (t) { + const { statistics } = t.context const expectedStats = { callCount: 3, totalTime: 0.306, @@ -120,7 +128,7 @@ describe('Stats', function () { statistics.recordValueInMillis(123, 34) statistics.recordValueInMillis(123, 34) - verifyStats(statistics, expectedStats) + t.verifyStats(statistics, expectedStats) const expectedStatsOther = { callCount: 2, @@ -135,7 +143,7 @@ describe('Stats', function () { other.recordValueInMillis(123, 0) other.recordValueInMillis(123, 0) - verifyStats(other, expectedStatsOther) + t.verifyStats(other, expectedStatsOther) const expectedStatsMerged = { callCount: 5, @@ -147,33 +155,41 @@ describe('Stats', function () { } statistics.merge(other) - verifyStats(statistics, expectedStatsMerged) + t.verifyStats(statistics, expectedStatsMerged) + t.end() }) - describe('when handling quantities', function () { - it('should store bytes as bytes, rescaling only at serialization') - it('should store time as nanoseconds, rescaling only at serialization') + t.test('when handling quantities', { todo: true }, function (t) { + t.test('should store bytes as bytes, rescaling only at serialization', { todo: true }) + t.test('should store time as nanoseconds, rescaling only at serialization', { todo: true }) }) - describe('recordValueInBytes', function () { + t.test('recordValueInBytes', function (t) { + t.autoend() const MEGABYTE = 1024 ** 2 - it('should measure bytes as megabytes', function () { + t.test('should measure bytes as megabytes', function (t) { + const { statistics } = t.context statistics.recordValueInBytes(MEGABYTE) - expect(statistics.total).equal(1) - expect(statistics.totalExclusive).equal(1) + t.equal(statistics.total, 1) + t.equal(statistics.totalExclusive, 1) + t.end() }) - it('should measure exclusive bytes ok', function () { + t.test('should measure exclusive bytes ok', function (t) { + const { statistics } = t.context statistics.recordValueInBytes(MEGABYTE * 2, MEGABYTE) - expect(statistics.total).equal(2) - expect(statistics.totalExclusive).equal(1) + t.equal(statistics.total, 2) + t.equal(statistics.totalExclusive, 1) + t.end() }) - it('should optionally not convert bytes to megabytes', function () { + t.test('should optionally not convert bytes to megabytes', function (t) { + const { statistics } = t.context statistics.recordValueInBytes(MEGABYTE * 2, MEGABYTE, true) - expect(statistics.total).equal(MEGABYTE * 2) - expect(statistics.totalExclusive).equal(MEGABYTE) + t.equal(statistics.total, MEGABYTE * 2) + t.equal(statistics.totalExclusive, MEGABYTE) + t.end() }) }) }) diff --git a/test/unit/test-helpers/metrics_helper.test.js b/test/unit/test-helpers/metrics_helper.test.js deleted file mode 100644 index 26fddc5691..0000000000 --- a/test/unit/test-helpers/metrics_helper.test.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect -const AssertionError = chai.AssertionError - -const metricsHelper = require('../../lib/metrics_helper.js') - -function MockSegment(name, children) { - this.name = name - this.children = [] - if (children) { - this.children = children - } -} - -describe('assertSegments', function () { - it('finds missing segment', function () { - const parent = new MockSegment('a') - const expected = ['b'] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - - expect(bound).to.throw(AssertionError, 'segment "a" should have child "b" in position 1') - }) - - it('finds missing segment among many', function () { - const parent = new MockSegment('a', [new MockSegment('b')]) - const expected = ['b', 'c'] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - expect(bound).to.throw(AssertionError, 'segment "a" should have child "c" in position 2') - }) - - it('finds missing segment deep', function () { - const parent = new MockSegment('a', [new MockSegment('b')]) - const expected = ['b', ['c']] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - expect(bound).to.throw(AssertionError, 'segment "b" should have child "c" in position 1') - }) - - it('finds extra segment', function () { - const parent = new MockSegment('a', [new MockSegment('b')]) - const expected = [] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - expect(bound).to.throw(AssertionError, 'segment "a" expected to have 0 children') - }) - - it('finds extra segment deep', function () { - const parent = new MockSegment('a', [ - new MockSegment('b', [new MockSegment('c'), new MockSegment('d')]) - ]) - const expected = ['b', ['c']] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - expect(bound).to.throw(AssertionError, 'segment "b" expected to have 1 children') - }) - - it('finds when segment has no children', function () { - const parent = new MockSegment('a', [new MockSegment('b', [new MockSegment('c')])]) - const expected = ['b'] - - const bound = metricsHelper.assertSegments.bind(null, parent, expected) - expect(bound).to.throw(AssertionError, 'segment "b" should not have any children') - }) - - it('ignores excluded segments', function () { - const parent = new MockSegment('a', [new MockSegment('b'), new MockSegment('c')]) - const expected = ['b'] - - const options = { - exact: true, - exclude: ['c'] - } - - const bound = metricsHelper.assertSegments.bind(null, parent, expected, options) - expect(bound).to.not.throw() - }) - - it('ignores excluded segments deep', function () { - const parent = new MockSegment('a', [ - new MockSegment('b', [new MockSegment('c'), new MockSegment('d', [new MockSegment('c')])]) - ]) - const expected = ['b', ['d']] - - const options = { - exact: true, - exclude: ['c'] - } - - const bound = metricsHelper.assertSegments.bind(null, parent, expected, options) - expect(bound).to.not.throw() - }) -}) diff --git a/test/unit/timer.test.js b/test/unit/timer.test.js index 341c7a3b48..9bbf8eee4b 100644 --- a/test/unit/timer.test.js +++ b/test/unit/timer.test.js @@ -4,84 +4,87 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const Timer = require('../../lib/timer') -describe('Timer', function () { - it("should know when it's active", function () { +tap.test('Timer', function (t) { + t.autoend() + t.test("should know when it's active", function (t) { const timer = new Timer() - expect(timer.isActive()).equal(true) + t.equal(timer.isActive(), true) + t.end() }) - it("should know when it hasn't yet been started", function () { + t.test("should know when it hasn't yet been started", function (t) { const timer = new Timer() - expect(timer.isRunning()).equal(false) + t.equal(timer.isRunning(), false) + t.end() }) - it("should know when it's running", function () { + t.test("should know when it's running", function (t) { const timer = new Timer() timer.begin() - expect(timer.isRunning()).equal(true) + t.equal(timer.isRunning(), true) + t.end() }) - it("should know when it's not running", function () { + t.test("should know when it's not running", function (t) { const timer = new Timer() - expect(timer.isRunning()).equal(false) + t.equal(timer.isRunning(), false) timer.begin() timer.end() - expect(timer.isRunning()).equal(false) + t.equal(timer.isRunning(), false) + t.end() }) - it("should know when it hasn't yet been stopped", function () { + t.test("should know when it hasn't yet been stopped", function (t) { const timer = new Timer() - expect(timer.isActive()).equal(true) + t.equal(timer.isActive(), true) timer.begin() - expect(timer.isActive()).equal(true) + t.equal(timer.isActive(), true) + t.end() }) - it("should know when it's stopped", function () { + t.test("should know when it's stopped", function (t) { const timer = new Timer() timer.begin() timer.end() - expect(timer.isActive()).equal(false) + t.equal(timer.isActive(), false) + t.end() }) - it('should return the time elapsed of a running timer', function (done) { + t.test('should return the time elapsed of a running timer', function (t) { const timer = new Timer() timer.begin() setTimeout(function () { - expect(timer.getDurationInMillis()).above(3) + t.ok(timer.getDurationInMillis() > 3) - return done() + t.end() }, 5) }) - it('should allow setting the start as well as the duration of the range', function () { + t.test('should allow setting the start as well as the duration of the range', function (t) { const timer = new Timer() const start = Date.now() timer.setDurationInMillis(5, start) - expect(timer.start).equal(start) + t.equal(timer.start, start) + t.end() }) - it('should return a range object', function () { + t.test('should return a range object', function (t) { const timer = new Timer() const start = Date.now() timer.setDurationInMillis(5, start) - expect(timer.toRange()).deep.equal([start, start + 5]) + t.same(timer.toRange(), [start, start + 5]) + t.end() }) - it('should calculate start times relative to other timers', function () { + t.test('should calculate start times relative to other timers', function (t) { const first = new Timer() first.begin() @@ -92,13 +95,14 @@ describe('Timer', function () { second.end() let delta - expect(function () { + t.doesNotThrow(function () { delta = second.startedRelativeTo(first) - }).not.throw() - expect(delta).a('number') + }) + t.ok(typeof delta === 'number') + t.end() }) - it('should support updating the duration with touch', function (done) { + t.test('should support updating the duration with touch', function (t) { const timer = new Timer() timer.begin() @@ -106,77 +110,88 @@ describe('Timer', function () { timer.touch() const first = timer.getDurationInMillis() - expect(first).above(0) - expect(timer.isActive()).equal(true) + t.ok(first > 0) + t.equal(timer.isActive(), true) setTimeout(function () { timer.end() const second = timer.getDurationInMillis() - expect(second).above(first) - expect(timer.isActive()).equal(false) + t.ok(second > first) + t.equal(timer.isActive(), false) - done() + t.end() }, 20) }, 20) }) - describe('endsAfter indicates whether the timer ended after another timer', () => { - let start - let first - let second - - beforeEach(function () { - start = Date.now() - first = new Timer() + t.test('endsAfter indicates whether the timer ended after another timer', (t) => { + t.autoend() + t.beforeEach(function (t) { + const start = Date.now() + const first = new Timer() first.setDurationInMillis(10, start) - second = new Timer() + t.context.second = new Timer() + t.context.start = start + t.context.first = first }) - it('with the same start and duration', function () { + t.test('with the same start and duration', function (t) { + const { start, second, first } = t.context second.setDurationInMillis(10, start) - expect(second.endsAfter(first)).equal(false) + t.equal(second.endsAfter(first), false) + t.end() }) - it('with longer duration', function () { + t.test('with longer duration', function (t) { + const { start, second, first } = t.context second.setDurationInMillis(11, start) - expect(second.endsAfter(first)).equal(true) + t.equal(second.endsAfter(first), true) + t.end() }) - it('with shorter duration', function () { + t.test('with shorter duration', function (t) { + const { start, second, first } = t.context second.setDurationInMillis(9, start) - expect(second.endsAfter(first)).equal(false) + t.equal(second.endsAfter(first), false) + t.end() }) - it('with earlier start', function () { + t.test('with earlier start', function (t) { + const { start, second, first } = t.context second.setDurationInMillis(10, start - 1) - expect(second.endsAfter(first)).equal(false) + t.equal(second.endsAfter(first), false) + t.end() }) - it('with later start', function () { + t.test('with later start', function (t) { + const { start, second, first } = t.context second.setDurationInMillis(10, start + 1) - expect(second.endsAfter(first)).equal(true) + t.equal(second.endsAfter(first), true) + t.end() }) }) - describe('overwriteDurationInMillis', function () { - it('stops the timer', function () { + t.test('overwriteDurationInMillis', function (t) { + t.autoend() + t.test('stops the timer', function (t) { const timer = new Timer() timer.begin() - expect(timer.isActive()).equal(true) + t.equal(timer.isActive(), true) timer.overwriteDurationInMillis(10) - expect(timer.isActive()).equal(false) + t.equal(timer.isActive(), false) + t.end() }) - it('overwrites duration recorded by end() and touch()', function (done) { + t.test('overwrites duration recorded by end() and touch()', function (t) { const timer = new Timer() timer.begin() setTimeout(function () { - expect(timer.getDurationInMillis() > 1).equal(true) + t.equal(timer.getDurationInMillis() > 1, true) timer.overwriteDurationInMillis(1) - expect(timer.getDurationInMillis()).equal(1) - done() + t.equal(timer.getDurationInMillis(), 1) + t.end() }, 2) }) }) diff --git a/test/unit/trace-aggregator.test.js b/test/unit/trace-aggregator.test.js index 2d71376059..c3371d3401 100644 --- a/test/unit/trace-aggregator.test.js +++ b/test/unit/trace-aggregator.test.js @@ -4,181 +4,94 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect -const should = chai.should() +const tap = require('tap') const helper = require('../lib/agent_helper') const configurator = require('../../lib/config') const TraceAggregator = require('../../lib/transaction/trace/aggregator') const Transaction = require('../../lib/transaction') -describe('TraceAggregator', function () { - let agent = null - - function createTransaction(name, duration, synth) { - const transaction = new Transaction(agent) - // gotta create the trace - transaction.trace.setDurationInMillis(duration) - transaction.url = name - transaction.name = 'WebTransaction/Uri' + name - transaction.statusCode = 200 - - if (synth) { - transaction.syntheticsData = { - version: 1, - accountId: 357, - resourceId: 'resId', - jobId: 'jobId', - monitorId: 'monId' - } +function createTransaction(agent, name, duration, synth) { + const transaction = new Transaction(agent) + // gotta create the trace + transaction.trace.setDurationInMillis(duration) + transaction.url = name + transaction.name = 'WebTransaction/Uri' + name + transaction.statusCode = 200 + + if (synth) { + transaction.syntheticsData = { + version: 1, + accountId: 357, + resourceId: 'resId', + jobId: 'jobId', + monitorId: 'monId' } + } + + return transaction.end() +} - return transaction.end() +function beforeEach(t) { + const agent = helper.loadMockedAgent({ run_id: 1337 }) + agent.collector._runLifecycle = (remote, payload, cb) => { + setImmediate(cb, null, [], { return_value: [] }) } + t.context.agent = agent +} - beforeEach(function () { - agent = helper.loadMockedAgent({ run_id: 1337 }) - agent.collector._runLifecycle = (remote, payload, cb) => { - setImmediate(cb, null, [], { return_value: [] }) - } - }) +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} - afterEach(function () { - helper.unloadAgent(agent) - }) +tap.test('TraceAggregator', function (t) { + t.autoend() + + t.beforeEach(beforeEach) + t.afterEach(afterEach) - it('should require a configuration at startup time', function () { - expect(() => new TraceAggregator()).to.throw() + t.test('should require a configuration at startup time', function (t) { + t.throws(() => new TraceAggregator()) const config = configurator.initialize({ transaction_tracer: { enabled: true } }) - expect(() => new TraceAggregator({ config })).to.not.throw() + t.doesNotThrow(() => new TraceAggregator({ config })) + t.end() }) - it("shouldn't collect a trace if the tracer is disabled", function () { + t.test("shouldn't collect a trace if the tracer is disabled", function (t) { + const { agent } = t.context agent.config.transaction_tracer.enabled = false - const tx = createTransaction('/test', 3000) + const tx = createTransaction(agent, '/test', 3000) agent.traces.add(tx) - expect(agent.traces.trace).to.not.exist + t.notOk(agent.traces.trace) + t.end() }) - it("shouldn't collect a trace if collect_traces is false", function () { + t.test("shouldn't collect a trace if collect_traces is false", function (t) { + const { agent } = t.context agent.config.collect_traces = false - const tx = createTransaction('/test', 3000) + const tx = createTransaction(agent, '/test', 3000) agent.traces.add(tx) - expect(agent.traces.trace).to.not.exist + t.notOk(agent.traces.trace) + t.end() }) - it('should let the agent decide whether to ignore a transaction', function () { + t.test('should let the agent decide whether to ignore a transaction', function (t) { + const { agent } = t.context const transaction = new Transaction(agent) transaction.trace.setDurationInMillis(3000) transaction.ignore = true agent.traces.add(transaction) - should.exist(agent.traces.trace) + t.ok(agent.traces.trace) + t.end() }) - describe('with top n support', function () { - let config - - beforeEach(function () { - config = configurator.initialize({ - transaction_tracer: { - enabled: true - } - }) - }) - - it('should set n from its configuration', function () { - const TOP_N = 21 - config.transaction_tracer.top_n = TOP_N - const aggregator = new TraceAggregator({ config }) - - expect(aggregator.capacity).equal(TOP_N) - }) - - it('should track the top 20 slowest transactions if top_n is unconfigured', () => { - const aggregator = new TraceAggregator({ config }) - - expect(aggregator.capacity).equal(20) - }) - - it('should track the slowest transaction in a harvest period if top_n is 0', () => { - config.transaction_tracer.top_n = 0 - const aggregator = new TraceAggregator({ config }) - - expect(aggregator.capacity).equal(1) - }) - - it('should only save a trace for an existing name if new one is slower', () => { - const URI = '/simple' - const aggregator = new TraceAggregator({ config }) - aggregator.reported = 10 // needed to override "first 5" - - aggregator.add(createTransaction(URI, 3000)) - aggregator.add(createTransaction(URI, 2100)) - expect(aggregator.requestTimes).to.have.property('WebTransaction/Uri/simple', 3000) - aggregator.add(createTransaction(URI, 4000)) - expect(aggregator.requestTimes).to.have.property('WebTransaction/Uri/simple', 4000) - }) - - it('should only track transactions for the top N names', function (done) { - agent.config.transaction_tracer.top_n = 5 - agent.traces.capacity = 5 - agent.traces.reported = 10 // needed to override "first 5" - const maxTraces = 6 - - const txnCreator = (n, max, cb) => { - expect(agent.traces.trace, 'trace before creation').to.not.exist - createTransaction(`/test-${n}`, 8000) - if (n !== 5) { - expect(agent.traces.trace, `trace ${n} to be collected`).to.exist - } else { - expect(agent.traces.trace, 'trace 5 collected').to.not.exist - } - agent.traces.once('finished transaction_sample_data data send.', (err) => - cb(err, { idx: n, max }) - ) - agent.traces.send() - expect(agent.traces.trace, 'trace after harvest').to.not.exist - } - const finalCallback = (err) => { - expect(err).to.not.exist - - const times = agent.traces.requestTimes - expect(times).to.have.property('WebTransaction/Uri/test-0', 8000) - expect(times).to.have.property('WebTransaction/Uri/test-1', 8000) - expect(times).to.have.property('WebTransaction/Uri/test-2', 8000) - expect(times).to.have.property('WebTransaction/Uri/test-3', 8000) - expect(times).to.have.property('WebTransaction/Uri/test-4', 8000) - expect(times).to.not.have.property('WebTransaction/Uri/test-5') - return done() - } - - const testCallback = (err, props) => { - expect(err, 'Callback error should be falsy.').to.not.exist - const { idx, max } = props - const nextIdx = idx + 1 - if (nextIdx >= max) { - return finalCallback() - } - return txnCreator(nextIdx, max, testCallback) - } - - // Step through recursively - txnCreator(0, maxTraces, testCallback) - }) - }) - - it('should collect traces when the threshold is 0', function () { + t.test('should collect traces when the threshold is 0', function (t) { + const { agent } = t.context const config = configurator.initialize({ transaction_tracer: { transaction_threshold: 0, @@ -196,10 +109,12 @@ describe('TraceAggregator', function () { transaction.statusCode = 200 aggregator.add(transaction) - expect(aggregator.requestTimes['WebTransaction/Uri/test']).equal(0) + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], 0) + t.end() }) - it('should collect traces for transactions that exceed apdex_f', function () { + t.test('should collect traces for transactions that exceed apdex_f', function (t) { + const { agent } = t.context const ABOVE_THRESHOLD = 29 const APDEXT = 0.007 @@ -223,10 +138,12 @@ describe('TraceAggregator', function () { transaction.statusCode = 200 aggregator.add(transaction) - expect(aggregator.requestTimes['WebTransaction/Uri/test']).equal(ABOVE_THRESHOLD) + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], ABOVE_THRESHOLD) + t.end() }) - it("should not collect traces for transactions that don't exceed apdex_f", function () { + t.test("should not collect traces for transactions that don't exceed apdex_f", function (t) { + const { agent } = t.context const BELOW_THRESHOLD = 27 const APDEXT = 0.007 @@ -250,10 +167,12 @@ describe('TraceAggregator', function () { transaction.statusCode = 200 aggregator.add(transaction) - expect(aggregator.requestTimes['WebTransaction/Uri/test']).equal(undefined) + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], undefined) + t.end() }) - it('should collect traces that exceed explicit trace threshold', () => { + t.test('should collect traces that exceed explicit trace threshold', (t) => { + const { agent } = t.context const ABOVE_THRESHOLD = 29 const THRESHOLD = 0.028 @@ -266,13 +185,15 @@ describe('TraceAggregator', function () { const aggregator = new TraceAggregator({ config }) aggregator.reported = 10 // needed to override "first 5" - const tx = createTransaction('/test', ABOVE_THRESHOLD) + const tx = createTransaction(agent, '/test', ABOVE_THRESHOLD) aggregator.add(tx) - expect(aggregator.requestTimes).to.have.property('WebTransaction/Uri/test', ABOVE_THRESHOLD) + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], ABOVE_THRESHOLD) + t.end() }) - it('should not collect traces that do not exceed trace threshold', () => { + t.test('should not collect traces that do not exceed trace threshold', (t) => { + const { agent } = t.context const BELOW_THRESHOLD = 29 const THRESHOLD = 30 @@ -285,12 +206,14 @@ describe('TraceAggregator', function () { const aggregator = new TraceAggregator({ config }) aggregator.reported = 10 // needed to override "first 5" - const tx = createTransaction('/test', BELOW_THRESHOLD) + const tx = createTransaction(agent, '/test', BELOW_THRESHOLD) aggregator.add(tx) - expect(aggregator.requestTimes).to.not.have.property('WebTransaction/Uri/test') + t.notOk(aggregator.requestTimes['WebTransaction/Uri/test']) + t.end() }) - it('should group transactions by the metric name associated with them', () => { + t.test('should group transactions by the metric name associated with them', (t) => { + const { agent } = t.context const config = configurator.initialize({ transaction_tracer: { enabled: true, @@ -300,12 +223,14 @@ describe('TraceAggregator', function () { const aggregator = new TraceAggregator({ config }) - const tx = createTransaction('/test', 2100) + const tx = createTransaction(agent, '/test', 2100) aggregator.add(tx) - expect(aggregator.requestTimes).to.have.property('WebTransaction/Uri/test', 2100) + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], 2100) + t.end() }) - it('should always report slow traces until 5 have been sent', function (done) { + t.test('should always report slow traces until 5 have been sent', function (t) { + const { agent } = t.context agent.config.apdex_t = 0 agent.config.run_id = 1337 agent.config.transaction_tracer.enabled = true @@ -315,9 +240,9 @@ describe('TraceAggregator', function () { // repeat! const txnCreator = (n, max, cb) => { - expect(agent.traces.trace, 'trace waiting to be collected').to.not.exist - createTransaction(`/test-${n % 3}`, 500) - expect(agent.traces.trace, `${n}th trace to collect`).to.exist + t.notOk(agent.traces.trace, 'trace waiting to be collected') + createTransaction(agent, `/test-${n % 3}`, 500) + t.ok(agent.traces.trace, `${n}th trace to collect`) agent.traces.once('finished transaction_sample_data data send.', (err) => cb(err, { idx: n, max }) ) @@ -325,17 +250,17 @@ describe('TraceAggregator', function () { } const finalCallback = (err) => { - expect(err).to.not.exist + t.error(err) // This 6th transaction should not be collected. - expect(agent.traces.trace).to.not.exist - createTransaction(`/test-0`, 500) - expect(agent.traces.trace, '6th trace to collect').to.not.exist - done() + t.notOk(agent.traces.trace) + createTransaction(agent, `/test-0`, 500) + t.notOk(agent.traces.trace, '6th trace to collect') + t.end() } // Array iteration is too difficult to slow down, so this steps through recursively txnCreator(0, maxTraces, function testCallback(err, props) { - expect(err, 'Callback error should be falsy.').to.not.exist + t.error(err) const { idx, max } = props const nextIdx = idx + 1 if (nextIdx >= max) { @@ -345,62 +270,168 @@ describe('TraceAggregator', function () { }) }) - describe('when request timings are tracked over time', function () { - it('should reset timings after 5 harvest cycles with no slow traces', (done) => { - agent.config.run_id = 1337 - agent.config.transaction_tracer.enabled = true - - const aggregator = agent.traces - const tx = createTransaction('/test', 5030) - aggregator.add(tx) - - let remaining = 4 - // 2nd-5th harvests: no serialized trace, timing still set - const looper = function () { - expect(aggregator.requestTimes['WebTransaction/Uri/test']).equal(5030) - aggregator.clear() - - remaining-- - if (remaining < 1) { - // 6th harvest: no serialized trace, timings reset - agent.traces.once('finished transaction_sample_data data send.', function () { - expect(aggregator.requestTimes).to.not.have.property('WebTransaction/Uri/test') - - done() - }) - agent.traces.send() - } else { - agent.traces.once('finished transaction_sample_data data send.', looper) - agent.traces.send() - } - } + t.test('should reset timings after 5 harvest cycles with no slow traces', (t) => { + const { agent } = t.context + agent.config.run_id = 1337 + agent.config.transaction_tracer.enabled = true + + const aggregator = agent.traces + const tx = createTransaction(agent, '/test', 5030) + aggregator.add(tx) - aggregator.add(tx) + let remaining = 4 + // 2nd-5th harvests: no serialized trace, timing still set + const looper = function () { + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], 5030) + aggregator.clear() - agent.traces.once('finished transaction_sample_data data send.', function () { - expect(aggregator.requestTimes['WebTransaction/Uri/test']).equal(5030) - aggregator.clear() + remaining-- + if (remaining < 1) { + // 6th harvest: no serialized trace, timings reset + agent.traces.once('finished transaction_sample_data data send.', function () { + t.notOk(aggregator.requestTimes['WebTransaction/Uri/test']) + t.end() + }) + agent.traces.send() + } else { agent.traces.once('finished transaction_sample_data data send.', looper) agent.traces.send() - }) + } + } + + aggregator.add(tx) + + agent.traces.once('finished transaction_sample_data data send.', function () { + t.equal(aggregator.requestTimes['WebTransaction/Uri/test'], 5030) + aggregator.clear() + + agent.traces.once('finished transaction_sample_data data send.', looper) agent.traces.send() }) + agent.traces.send() }) - it('should reset the syntheticsTraces when resetting trace', function () { + t.test('should reset the syntheticsTraces when resetting trace', function (t) { + const { agent } = t.context agent.config.transaction_tracer.enabled = true const aggregator = agent.traces - createTransaction('/testOne', 503) - expect(aggregator.trace).to.exist + createTransaction(agent, '/testOne', 503) + t.ok(aggregator.trace) aggregator.clear() - createTransaction('/testTwo', 406, true) - expect(aggregator.trace).to.not.exist - expect(aggregator.syntheticsTraces).to.have.length(1) + createTransaction(agent, '/testTwo', 406, true) + t.notOk(aggregator.trace) + t.equal(aggregator.syntheticsTraces.length, 1) aggregator.clear() - expect(aggregator.syntheticsTraces).to.have.length(0) + t.equal(aggregator.syntheticsTraces.length, 0) + t.end() + }) +}) + +tap.test('TraceAggregator with top n support', function (t) { + t.autoend() + t.beforeEach(function () { + beforeEach(t) + t.context.config = configurator.initialize({ + transaction_tracer: { + enabled: true + } + }) + }) + + t.afterEach(afterEach) + + t.test('should set n from its configuration', function (t) { + const { config } = t.context + const TOP_N = 21 + config.transaction_tracer.top_n = TOP_N + const aggregator = new TraceAggregator({ config }) + + t.equal(aggregator.capacity, TOP_N) + t.end() + }) + + t.test('should track the top 20 slowest transactions if top_n is unconfigured', (t) => { + const { config } = t.context + const aggregator = new TraceAggregator({ config }) + + t.equal(aggregator.capacity, 20) + t.end() + }) + + t.test('should track the slowest transaction in a harvest period if top_n is 0', (t) => { + const { config } = t.context + config.transaction_tracer.top_n = 0 + const aggregator = new TraceAggregator({ config }) + + t.equal(aggregator.capacity, 1) + t.end() + }) + + t.test('should only save a trace for an existing name if new one is slower', (t) => { + const { config, agent } = t.context + const URI = '/simple' + const aggregator = new TraceAggregator({ config }) + aggregator.reported = 10 // needed to override "first 5" + + aggregator.add(createTransaction(agent, URI, 3000)) + aggregator.add(createTransaction(agent, URI, 2100)) + t.equal(aggregator.requestTimes['WebTransaction/Uri/simple'], 3000) + aggregator.add(createTransaction(agent, URI, 4000)) + t.equal(aggregator.requestTimes['WebTransaction/Uri/simple'], 4000) + t.end() + }) + + t.test('should only track transactions for the top N names', function (t) { + const { agent } = t.context + agent.config.transaction_tracer.top_n = 5 + agent.traces.capacity = 5 + agent.traces.reported = 10 // needed to override "first 5" + const maxTraces = 6 + + const txnCreator = (n, max, cb) => { + t.notOk(agent.traces.trace, 'trace before creation') + createTransaction(agent, `/test-${n}`, 8000) + if (n !== 5) { + t.ok(agent.traces.trace, `trace ${n} to be collected`) + } else { + t.notOk(agent.traces.trace, 'trace 5 collected') + } + agent.traces.once('finished transaction_sample_data data send.', (err) => + cb(err, { idx: n, max }) + ) + agent.traces.send() + t.notOk(agent.traces.trace, 'trace after harvest') + if (n === 5) { + t.end() + } + } + const finalCallback = (err) => { + t.error(err) + + const times = agent.traces.requestTimes + t.equal(times['WebTransaction/Uri/test-0'], 8000) + t.equal(times['WebTransaction/Uri/test-1'], 8000) + t.equal(times['WebTransaction/Uri/test-2'], 8000) + t.equal(times['WebTransaction/Uri/test-3'], 8000) + t.equal(times['WebTransaction/Uri/test-4'], 8000) + t.notOk(times['WebTransaction/Uri/test-5']) + } + + const testCallback = (err, props) => { + t.error(err) + const { idx, max } = props + const nextIdx = idx + 1 + if (nextIdx >= max) { + return finalCallback() + } + return txnCreator(nextIdx, max, testCallback) + } + + // Step through recursively + txnCreator(0, maxTraces, testCallback) }) }) diff --git a/test/unit/tracer.test.js b/test/unit/tracer.test.js index 153d058ec7..6cc057056b 100644 --- a/test/unit/tracer.test.js +++ b/test/unit/tracer.test.js @@ -4,122 +4,142 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const chai = require('chai') -const expect = chai.expect +const tap = require('tap') const helper = require('../lib/agent_helper') const Segment = require('../../lib/transaction/trace/segment') const notRunningStates = ['stopped', 'stopping', 'errored'] - -describe('Tracer', function () { - let agent = null - let tracer = null - - beforeEach(function () { - agent = helper.loadMockedAgent() - tracer = agent.tracer - }) - - afterEach(function () { - helper.unloadAgent(agent) - }) - - describe('#transactionProxy', () => { - it('should create transaction', () => { +function beforeEach(t) { + const agent = helper.loadMockedAgent() + t.context.tracer = agent.tracer + t.context.agent = agent +} + +function afterEach(t) { + helper.unloadAgent(t.context.agent) +} + +tap.test('Tracer', function (t) { + t.autoend() + + t.test('#transactionProxy', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should create transaction', (t) => { + const { tracer } = t.context const wrapped = tracer.transactionProxy(() => { const transaction = tracer.getTransaction() - expect(transaction).to.exist + t.ok(transaction) + t.end() }) wrapped() }) - it('should not try to wrap a null handler', function () { - expect(tracer.transactionProxy(null)).equal(null) + t.test('should not try to wrap a null handler', function (t) { + const { tracer } = t.context + t.equal(tracer.transactionProxy(null), null) + t.end() }) notRunningStates.forEach((agentState) => { - it(`should not create transaction when agent state is ${agentState}`, () => { + t.test(`should not create transaction when agent state is ${agentState}`, (t) => { + const { tracer, agent } = t.context agent.setState(agentState) const wrapped = tracer.transactionProxy(() => { const transaction = tracer.getTransaction() - expect(transaction).to.not.exist + t.notOk(transaction) }) wrapped() + t.end() }) }) }) - describe('#transactionNestProxy', () => { - it('should create transaction', () => { + t.test('#transactionNestProxy', (t) => { + t.autoend() + t.beforeEach(beforeEach) + t.afterEach(afterEach) + t.test('should create transaction', (t) => { + const { tracer } = t.context const wrapped = tracer.transactionNestProxy('web', () => { const transaction = tracer.getTransaction() - expect(transaction).to.exist + t.ok(transaction) }) wrapped() + t.end() }) notRunningStates.forEach((agentState) => { - it(`should not create transaction when agent state is ${agentState}`, () => { + t.test(`should not create transaction when agent state is ${agentState}`, (t) => { + const { tracer, agent } = t.context agent.setState(agentState) const wrapped = tracer.transactionNestProxy('web', () => { const transaction = tracer.getTransaction() - expect(transaction).to.not.exist + t.notOk(transaction) }) wrapped() + t.end() }) }) - describe('when proxying a trace segment', function () { - it('should not try to wrap a null handler', function () { - helper.runInTransaction(agent, function () { - expect(tracer.wrapFunction('123', null, null)).equal(null) - }) + t.test('when proxying a trace segment should not try to wrap a null handler', function (t) { + const { tracer, agent } = t.context + helper.runInTransaction(agent, function () { + t.equal(tracer.wrapFunction('123', null, null), null) + t.end() }) }) - describe('when proxying a callback', function () { - it('should not try to wrap a null handler', function () { - helper.runInTransaction(agent, function () { - expect(tracer.bindFunction(null)).equal(null) - }) + t.test('when proxying a callback should not try to wrap a null handler', function (t) { + const { tracer, agent } = t.context + helper.runInTransaction(agent, function () { + t.equal(tracer.bindFunction(null), null) + t.end() }) }) - describe('when handling immutable errors', function () { - it('should not break in annotation process', function () { - helper.runInTransaction(agent, function (trans) { - function wrapMe() { - const err = new Error('FIREBOMB') - Object.freeze(err) - throw err - } - expect(tracer.bindFunction(wrapMe, new Segment(trans, 'name'))).throws() - }) + t.test('when handling immutable errors should not break in annotation process', function (t) { + const expectErrMsg = 'FIREBOMB' + const { tracer, agent } = t.context + helper.runInTransaction(agent, function (trans) { + function wrapMe() { + const err = new Error(expectErrMsg) + Object.freeze(err) + throw err + } + try { + // cannot use `t.throws` because we instrument things within the function + // so the original throws then another throws and tap does not like that + const fn = tracer.bindFunction(wrapMe, new Segment(trans, 'name')) + fn() + } catch (err) { + t.equal(err.message, expectErrMsg) + t.end() + } }) }) - describe('when a transaction is created inside a transaction', function () { - it('should reuse the existing transaction instead of nesting', function () { + t.test( + 'when a transaction is created inside a transaction should reuse the existing transaction instead of nesting', + function (t) { + const { agent } = t.context helper.runInTransaction(agent, function (outerTransaction) { const outerId = outerTransaction.id helper.runInTransaction(agent, function (innerTransaction) { const innerId = innerTransaction.id - expect(innerId).equal(outerId) + t.equal(innerId, outerId) + t.end() }) }) - }) - }) + } + ) }) }) diff --git a/test/unit/transaction-event-aggregator.test.js b/test/unit/transaction-event-aggregator.test.js index 90c49fb766..1794666912 100644 --- a/test/unit/transaction-event-aggregator.test.js +++ b/test/unit/transaction-event-aggregator.test.js @@ -4,12 +4,7 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect +const tap = require('tap') const TransactionEventAggregator = require('../../lib/transaction/transaction-event-aggregator') const Metrics = require('../../lib/metrics') @@ -18,36 +13,36 @@ const LIMIT = 5 const EXPECTED_METHOD = 'analytic_event_data' const SPLIT_THRESHOLD = 3 -describe('Transaction Event Aggregator', () => { - let eventAggregator - let fakeCollectorApi = null - - beforeEach(() => { - fakeCollectorApi = {} - fakeCollectorApi[EXPECTED_METHOD] = () => {} - - eventAggregator = new TransactionEventAggregator( - { - runId: RUN_ID, - limit: LIMIT, - splitThreshold: SPLIT_THRESHOLD - }, - fakeCollectorApi, - new Metrics(5, {}, {}) - ) - }) +function beforeEach(t) { + const fakeCollectorApi = {} + fakeCollectorApi[EXPECTED_METHOD] = () => {} + + t.context.eventAggregator = new TransactionEventAggregator( + { + runId: RUN_ID, + limit: LIMIT, + splitThreshold: SPLIT_THRESHOLD + }, + fakeCollectorApi, + new Metrics(5, {}, {}) + ) + t.context.fakeCollectorApi = fakeCollectorApi +} - afterEach(() => { - eventAggregator = null - }) +tap.test('Transaction Event Aggregator', (t) => { + t.autoend() + t.beforeEach(beforeEach) - it('should set the correct default method', () => { + t.test('should set the correct default method', (t) => { + const { eventAggregator } = t.context const method = eventAggregator.method - expect(method).to.equal(EXPECTED_METHOD) + t.equal(method, EXPECTED_METHOD) + t.end() }) - it('toPayload() should return json format of data', () => { + t.test('toPayload() should return json format of data', (t) => { + const { eventAggregator } = t.context const expectedMetrics = { reservoir_size: LIMIT, events_seen: 1 @@ -58,146 +53,162 @@ describe('Transaction Event Aggregator', () => { eventAggregator.add(rawEvent) const payload = eventAggregator._toPayloadSync() - expect(payload.length).to.equal(3) + t.equal(payload.length, 3) const [runId, eventMetrics, eventData] = payload - expect(runId).to.equal(RUN_ID) - expect(eventMetrics).to.deep.equal(expectedMetrics) - expect(eventData).to.deep.equal([rawEvent]) + t.equal(runId, RUN_ID) + t.same(eventMetrics, expectedMetrics) + t.same(eventData, [rawEvent]) + t.end() }) - it('toPayload() should return nothing with no event data', () => { + t.test('toPayload() should return nothing with no event data', (t) => { + const { eventAggregator } = t.context const payload = eventAggregator._toPayloadSync() - expect(payload).to.not.exist + t.notOk(payload) + t.end() }) +}) - describe('when data over split threshold', () => { - beforeEach(() => { - eventAggregator.add([{ type: 'Transaction', error: false }, { num: 1 }]) - eventAggregator.add([{ type: 'Transaction', error: false }, { num: 2 }]) - eventAggregator.add([{ type: 'Transaction', error: false }, { num: 3 }]) - eventAggregator.add([{ type: 'Transaction', error: false }, { num: 4 }]) - eventAggregator.add([{ type: 'Transaction', error: false }, { num: 5 }]) - }) +tap.test('Transaction Event Aggregator - when data over split threshold', (t) => { + t.autoend() + t.beforeEach((t) => { + beforeEach(t) + const { eventAggregator } = t.context + eventAggregator.add([{ type: 'Transaction', error: false }, { num: 1 }]) + eventAggregator.add([{ type: 'Transaction', error: false }, { num: 2 }]) + eventAggregator.add([{ type: 'Transaction', error: false }, { num: 3 }]) + eventAggregator.add([{ type: 'Transaction', error: false }, { num: 4 }]) + eventAggregator.add([{ type: 'Transaction', error: false }, { num: 5 }]) + }) - describe('send()', () => { - it('should emit proper message with method for starting send', (done) => { - const expectedStartEmit = `starting ${EXPECTED_METHOD} data send.` + t.test('should emit proper message with method for starting send', (t) => { + const { eventAggregator } = t.context + const expectedStartEmit = `starting ${EXPECTED_METHOD} data send.` - eventAggregator.once(expectedStartEmit, done) + eventAggregator.once(expectedStartEmit, t.end) - eventAggregator.send() - }) + eventAggregator.send() + }) - it('should clear existing data', () => { - eventAggregator.send() + t.test('should clear existing data', (t) => { + const { eventAggregator } = t.context + eventAggregator.send() - expect(eventAggregator.events.length).to.equal(0) - }) + t.equal(eventAggregator.events.length, 0) + t.end() + }) - it('should call transport for two payloads', () => { - const payloads = [] + t.test('should call transport for two payloads', (t) => { + const { eventAggregator, fakeCollectorApi } = t.context + const payloads = [] - fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { - payloads.push(payload) + fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { + payloads.push(payload) - // Needed for both to invoke - callback(null, { retainData: false }) - } + // Needed for both to invoke + callback(null, { retainData: false }) + } - eventAggregator.send() + eventAggregator.send() - expect(payloads.length).to.equal(2) + t.equal(payloads.length, 2) - const [firstPayload, secondPayload] = payloads + const [firstPayload, secondPayload] = payloads - const [firstRunId, firstMetrics, firstEventData] = firstPayload - expect(firstRunId).to.equal(RUN_ID) - expect(firstMetrics).to.deep.equal({ - reservoir_size: 2, - events_seen: 2 - }) - expect(firstEventData.length).to.equal(2) + const [firstRunId, firstMetrics, firstEventData] = firstPayload + t.equal(firstRunId, RUN_ID) + t.same(firstMetrics, { + reservoir_size: 2, + events_seen: 2 + }) + t.equal(firstEventData.length, 2) - const [secondRunId, secondMetrics, secondEventData] = secondPayload - expect(secondRunId).to.equal(RUN_ID) - expect(secondMetrics).to.deep.equal({ - reservoir_size: 3, - events_seen: 3 - }) - expect(secondEventData.length).to.equal(3) - }) + const [secondRunId, secondMetrics, secondEventData] = secondPayload + t.equal(secondRunId, RUN_ID) + t.same(secondMetrics, { + reservoir_size: 3, + events_seen: 3 + }) + t.equal(secondEventData.length, 3) + t.end() + }) - it('should call merge with original data when transport indicates retain', () => { - const originalData = eventAggregator._getMergeData() + t.test('should call merge with original data when transport indicates retain', (t) => { + const { eventAggregator, fakeCollectorApi } = t.context + const originalData = eventAggregator._getMergeData() - fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { - callback(null, { retainData: true }) - } + fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { + callback(null, { retainData: true }) + } - eventAggregator.send() + eventAggregator.send() - const currentData = eventAggregator._getMergeData() - expect(currentData.length).to.equal(originalData.length) + const currentData = eventAggregator._getMergeData() + t.equal(currentData.length, originalData.length) - const originalEvents = originalData.toArray().sort(sortEventsByNum) - const currentEvents = currentData.toArray().sort(sortEventsByNum) + const originalEvents = originalData.toArray().sort(sortEventsByNum) + const currentEvents = currentData.toArray().sort(sortEventsByNum) - expect(currentEvents).to.deep.equal(originalEvents) - }) + t.same(currentEvents, originalEvents) + t.end() + }) - it('should not merge when transport indicates not to retain', () => { - fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { - callback(null, { retainData: false }) - } + t.test('should not merge when transport indicates not to retain', (t) => { + const { eventAggregator, fakeCollectorApi } = t.context + fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { + callback(null, { retainData: false }) + } - eventAggregator.send() + eventAggregator.send() - const currentData = eventAggregator._getMergeData() + const currentData = eventAggregator._getMergeData() - expect(currentData.length).to.equal(0) - }) + t.equal(currentData.length, 0) + t.end() + }) - it('should handle payload retain values individually', () => { - let payloadCount = 0 - let payloadToRetain = null - fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { - payloadCount++ + t.test('should handle payload retain values individually', (t) => { + const { eventAggregator, fakeCollectorApi } = t.context + let payloadCount = 0 + let payloadToRetain = null + fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { + payloadCount++ - const shouldRetain = payloadCount > 1 - if (shouldRetain) { - payloadToRetain = payload - } + const shouldRetain = payloadCount > 1 + if (shouldRetain) { + payloadToRetain = payload + } - callback(null, { retainData: shouldRetain }) - } + callback(null, { retainData: shouldRetain }) + } - eventAggregator.send() + eventAggregator.send() - const eventsToRetain = payloadToRetain[2].sort(sortEventsByNum) + const eventsToRetain = payloadToRetain[2].sort(sortEventsByNum) - const currentData = eventAggregator._getMergeData() - expect(currentData.length).to.equal(eventsToRetain.length) + const currentData = eventAggregator._getMergeData() + t.equal(currentData.length, eventsToRetain.length) - const currentEvents = currentData.toArray().sort(sortEventsByNum) + const currentEvents = currentData.toArray().sort(sortEventsByNum) - expect(currentEvents).to.deep.equal(eventsToRetain) - }) + t.same(currentEvents, eventsToRetain) + t.end() + }) - it('should emit proper message with method for finishing send', (done) => { - const expectedStartEmit = `finished ${EXPECTED_METHOD} data send.` + t.test('should emit proper message with method for finishing send', (t) => { + const { eventAggregator, fakeCollectorApi } = t.context + const expectedStartEmit = `finished ${EXPECTED_METHOD} data send.` - eventAggregator.once(expectedStartEmit, done) + eventAggregator.once(expectedStartEmit, t.end) - fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { - callback(null, { retainData: false }) - } + fakeCollectorApi[EXPECTED_METHOD] = (payload, callback) => { + callback(null, { retainData: false }) + } - eventAggregator.send() - }) - }) + eventAggregator.send() }) }) diff --git a/test/unit/urltils.test.js b/test/unit/urltils.test.js index 6687cb384a..f4376a0378 100644 --- a/test/unit/urltils.test.js +++ b/test/unit/urltils.test.js @@ -4,249 +4,322 @@ */ 'use strict' - -// TODO: convert to normal tap style. -// Below allows use of mocha DSL with tap runner. -require('tap').mochaGlobals() - -const expect = require('chai').expect +const tap = require('tap') const sinon = require('sinon') const proxyquire = require('proxyquire') const url = require('url') -describe('NR URL utilities', function () { - let loggerStub - let urltils - beforeEach(function () { - loggerStub = { +tap.test('NR URL utilities', function (t) { + t.autoend() + t.beforeEach(function () { + const loggerStub = { warn: sinon.stub() } - urltils = proxyquire('../../lib/util/urltils', { + t.context.urltils = proxyquire('../../lib/util/urltils', { '../logger': { child: sinon.stub().returns(loggerStub) } }) + t.context.loggerStub = loggerStub }) - describe('scrubbing URLs', function () { - it('should return "/" if there\'s no leading slash on the path', function () { - expect(urltils.scrub('?t_u=http://some.com/o/p')).equal('/') - }) + t.test('scrubbing URLs should return "/" if there\'s no leading slash on the path', function (t) { + const { urltils } = t.context + t.equal(urltils.scrub('?t_u=http://some.com/o/p'), '/') + t.end() }) - describe('parsing parameters', function () { - it('should find empty object of params in url lacking query', function () { - expect(urltils.parseParameters('/favicon.ico')).deep.equal({}) + t.test('parsing parameters', function (t) { + t.autoend() + t.test('should find empty object of params in url lacking query', function (t) { + const { urltils } = t.context + t.same(urltils.parseParameters('/favicon.ico'), {}) + t.end() }) - it('should find v param in url containing ?v with no value', function () { - expect(urltils.parseParameters('/status?v')).deep.equal({ v: true }) + t.test('should find v param in url containing ?v with no value', function (t) { + const { urltils } = t.context + t.same(urltils.parseParameters('/status?v'), { v: true }) + t.end() }) - it('should find v param with value in url containing ?v=1', function () { - expect(urltils.parseParameters('/status?v=1')).deep.equal({ v: '1' }) + t.test('should find v param with value in url containing ?v=1', function (t) { + const { urltils } = t.context + t.same(urltils.parseParameters('/status?v=1'), { v: '1' }) + t.end() }) - it('should find v param when passing in an object', function () { - expect(urltils.parseParameters(url.parse('/status?v=1', true))).deep.equal({ v: '1' }) + t.test('should find v param when passing in an object', function (t) { + const { urltils } = t.context + t.same(urltils.parseParameters(url.parse('/status?v=1', true)), { v: '1' }) + t.end() }) }) - describe('determining whether an HTTP status code is an error', function () { + t.test('determining whether an HTTP status code is an error', function (t) { + t.autoend() let config = { error_collector: { ignore_status_codes: [] } } - it('should not throw when called with no params', function () { - expect(function () { + t.test('should not throw when called with no params', function (t) { + const { urltils } = t.context + t.doesNotThrow(function () { urltils.isError() - }).not.throws() + }) + t.end() }) - it('should not throw when called with no code', function () { - expect(function () { + t.test('should not throw when called with no code', function (t) { + const { urltils } = t.context + t.doesNotThrow(function () { urltils.isError(config) - }).not.throws() + }) + t.end() }) - it('should not throw when config is missing', function () { - expect(function () { + t.test('should not throw when config is missing', function (t) { + const { urltils } = t.context + t.doesNotThrow(function () { urltils.isError(null, 200) - }).not.throws() + }) + t.end() }) - it('should NOT mark an OK request as an error', function () { - return expect(urltils.isError(config, 200)).false + t.test('should NOT mark an OK request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 200), false) + t.end() }) - it('should NOT mark a permanent redirect as an error', function () { - return expect(urltils.isError(config, 301)).false + t.test('should NOT mark a permanent redirect as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 301), false) + t.end() }) - it('should NOT mark a temporary redirect as an error', function () { - return expect(urltils.isError(config, 303)).false + t.test('should NOT mark a temporary redirect as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 303), false) + t.end() }) - it('should mark a bad request as an error', function () { - return expect(urltils.isError(config, 400)).true + t.test('should mark a bad request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 400), true) + t.end() }) - it('should mark an unauthorized request as an error', function () { - return expect(urltils.isError(config, 401)).true + t.test('should mark an unauthorized request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 401), true) + t.end() }) - it('should mark a "payment required" request as an error', function () { - return expect(urltils.isError(config, 402)).true + t.test('should mark a "payment required" request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 402), true) + t.end() }) - it('should mark a forbidden request as an error', function () { - return expect(urltils.isError(config, 403)).true + t.test('should mark a forbidden request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 403), true) + t.end() }) - it('should mark a not found request as an error', function () { - return expect(urltils.isError(config, 404)).true + t.test('should mark a not found request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 404), true) + t.end() }) - it('should mark a request with too long a URI as an error', function () { - return expect(urltils.isError(config, 414)).true + t.test('should mark a request with too long a URI as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 414), true) + t.end() }) - it('should mark a method not allowed request as an error', function () { - return expect(urltils.isError(config, 405)).true + t.test('should mark a method not allowed request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 405), true) + t.end() }) - it('should mark a request with unacceptable types as an error', function () { - return expect(urltils.isError(config, 406)).true + t.test('should mark a request with unacceptable types as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 406), true) + t.end() }) - it('should mark a request requiring proxy auth as an error', function () { - return expect(urltils.isError(config, 407)).true + t.test('should mark a request requiring proxy auth as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 407), true) + t.end() }) - it('should mark a timed out request as an error', function () { - return expect(urltils.isError(config, 408)).true + t.test('should mark a timed out request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 408), true) + t.end() }) - it('should mark a conflicted request as an error', function () { - return expect(urltils.isError(config, 409)).true + t.test('should mark a conflicted request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 409), true) + t.end() }) - it('should mark a request for a disappeared resource as an error', function () { - return expect(urltils.isError(config, 410)).true + t.test('should mark a request for a disappeared resource as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 410), true) + t.end() }) - it('should mark a request with a missing length as an error', function () { - return expect(urltils.isError(config, 411)).true + t.test('should mark a request with a missing length as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 411), true) + t.end() }) - it('should mark a request with a failed precondition as an error', function () { - return expect(urltils.isError(config, 412)).true + t.test('should mark a request with a failed precondition as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 412), true) + t.end() }) - it('should mark a too-large request as an error', function () { - return expect(urltils.isError(config, 413)).true + t.test('should mark a too-large request as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 413), true) + t.end() }) - it('should mark a request for an unsupported media type as an error', function () { - return expect(urltils.isError(config, 415)).true + t.test('should mark a request for an unsupported media type as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 415), true) + t.end() }) - it('should mark a request for an unsatisfiable range as an error', function () { - return expect(urltils.isError(config, 416)).true + t.test('should mark a request for an unsatisfiable range as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 416), true) + t.end() }) - it('should mark a request with a failed expectation as an error', function () { - return expect(urltils.isError(config, 417)).true + t.test('should mark a request with a failed expectation as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 417), true) + t.end() }) - it('should mark a request asserting teapotness as an error', function () { - return expect(urltils.isError(config, 418)).true + t.test('should mark a request asserting teapotness as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 418), true) + t.end() }) - it('should mark a request with timed-out auth as an error', function () { - return expect(urltils.isError(config, 419)).true + t.test('should mark a request with timed-out auth as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 419), true) + t.end() }) - it('should mark a request for enhanced calm (brah) as an error', function () { - return expect(urltils.isError(config, 420)).true + t.test('should mark a request for enhanced calm (brah) as an error', function (t) { + const { urltils } = t.context + t.equal(urltils.isError(config, 420), true) + t.end() }) - it('should work with strings', function () { + t.test('should work with strings', function (t) { + const { urltils } = t.context config = { error_collector: { ignore_status_codes: [403] } } - expect(urltils.isError(config, '200')).to.be.false - expect(urltils.isError(config, '403')).to.be.false - expect(urltils.isError(config, '404')).to.be.true + t.equal(urltils.isError(config, '200'), false) + t.equal(urltils.isError(config, '403'), false) + t.equal(urltils.isError(config, '404'), true) + t.end() }) }) - describe('isIgnoredError', function () { + t.test('isIgnoredError', function (t) { + t.autoend() const config = { error_collector: { ignore_status_codes: [] } } - it('returns true if the status code is an HTTP error in the ignored list', () => { + t.test('returns true if the status code is an HTTP error in the ignored list', (t) => { + const { urltils } = t.context const errorCodes = [ 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 500, 503 ] errorCodes.forEach((code) => { - expect(urltils.isIgnoredError(config, code)).equal(false) + t.equal(urltils.isIgnoredError(config, code), false) config.error_collector.ignore_status_codes = [code] - expect(urltils.isIgnoredError(config, code)).equal(true) + t.equal(urltils.isIgnoredError(config, code), true) }) + t.end() }) - it('returns false if the status code is NOT an HTTP error', function () { + t.test('returns false if the status code is NOT an HTTP error', function (t) { + const { urltils } = t.context const statusCodes = [200] statusCodes.forEach((code) => { - expect(urltils.isIgnoredError(config, code)).equal(false) + t.equal(urltils.isIgnoredError(config, code), false) config.error_collector.ignore_status_codes = [code] - expect(urltils.isIgnoredError(config, code)).equal(false) + t.equal(urltils.isIgnoredError(config, code), false) }) + t.end() }) }) - describe('copying parameters from a query hash', function () { - let source - let dest - - beforeEach(function () { - source = {} - dest = {} + t.test('copying parameters from a query hash', function (t) { + t.autoend() + t.beforeEach(function (t) { + t.context.source = {} + t.context.dest = {} }) - it('shouldn not throw on missing configuration', function () { - expect(function () { + t.test("shouldn't not throw on missing configuration", function (t) { + const { urltils, source, dest } = t.context + t.doesNotThrow(function () { urltils.copyParameters(null, source, dest) - }).not.throws() + }) + t.end() }) - it('should not throw on missing source', function () { - expect(function () { + t.test('should not throw on missing source', function (t) { + const { urltils, dest } = t.context + t.doesNotThrow(function () { urltils.copyParameters(null, dest) - }).not.throws() + }) + t.end() }) - it('should not throw on missing destination', function () { - expect(function () { + t.test('should not throw on missing destination', function (t) { + const { urltils, source } = t.context + t.doesNotThrow(function () { urltils.copyParameters(source, null) - }).not.throws() + }) + t.end() }) - it('should copy parameters from source to destination', function () { + t.test('should copy parameters from source to destination', function (t) { + const { urltils, source, dest } = t.context dest.existing = 'here' source.firstNew = 'present' source.secondNew = 'accounted for' - expect(function () { + t.doesNotThrow(function () { urltils.copyParameters(source, dest) - }).not.throws() + }) - expect(dest).eql({ + t.same(dest, { existing: 'here', firstNew: 'present', secondNew: 'accounted for' }) + t.end() }) - it('should not overwrite existing parameters in destination', function () { + t.test('should not overwrite existing parameters in destination', function (t) { + const { urltils, source, dest } = t.context dest.existing = 'here' dest.firstNew = 'already around' source.firstNew = 'present' @@ -254,46 +327,49 @@ describe('NR URL utilities', function () { urltils.copyParameters(source, dest) - expect(dest).eql({ + t.same(dest, { existing: 'here', firstNew: 'already around', secondNew: 'accounted for' }) + t.end() }) - it('should not overwrite null parameters in destination', function () { + t.test('should not overwrite null parameters in destination', function (t) { + const { urltils, source, dest } = t.context dest.existing = 'here' dest.firstNew = null source.firstNew = 'present' urltils.copyParameters(source, dest) - expect(dest).eql({ + t.same(dest, { existing: 'here', firstNew: null }) + t.end() }) - it('should not overwrite undefined parameters in destination', function () { + t.test('should not overwrite undefined parameters in destination', function (t) { + const { urltils, source, dest } = t.context dest.existing = 'here' dest.firstNew = undefined source.firstNew = 'present' urltils.copyParameters(source, dest) - expect(dest).eql({ + t.same(dest, { existing: 'here', firstNew: undefined }) + t.end() }) }) - describe('obfuscates path by regex', function () { - let config - let path - - beforeEach(() => { - config = { + t.test('obfuscates path by regex', function (t) { + t.autoend() + t.beforeEach((t) => { + t.context.config = { url_obfuscation: { enabled: false, regex: { @@ -303,59 +379,84 @@ describe('NR URL utilities', function () { } } } - path = '/foo/123/bar/456/baz/789' + t.context.path = '/foo/123/bar/456/baz/789' }) - it('should not obfuscate path by default', function () { - expect(urltils.obfuscatePath(config, path)).equal(path) + t.test('should not obfuscate path by default', function (t) { + const { urltils, config, path } = t.context + t.equal(urltils.obfuscatePath(config, path), path) + t.end() }) - it('should not obfuscate if obfuscation is enabled but pattern is not set', function () { + t.test('should not obfuscate if obfuscation is enabled but pattern is not set', function (t) { + const { urltils, config, path } = t.context config.url_obfuscation.enabled = true - expect(urltils.obfuscatePath(config, path)).equal(path) + t.equal(urltils.obfuscatePath(config, path), path) + t.end() }) - it('should not obfuscate if obfuscation is enabled but pattern is invalid', function () { + t.test('should not obfuscate if obfuscation is enabled but pattern is invalid', function (t) { + const { urltils, config, path } = t.context config.url_obfuscation.enabled = true config.url_obfuscation.regex.pattern = '/foo/bar/baz/[0-9]+' - expect(urltils.obfuscatePath(config, path)).equal(path) - }) - - it('should obfuscate with empty string `` if replacement is not set and pattern is set', function () { - config.url_obfuscation.enabled = true - config.url_obfuscation.regex.pattern = '/foo/[0-9]+/bar/[0-9]+/baz/[0-9]+' - expect(urltils.obfuscatePath(config, path)).equal('') - }) - - it('should obfuscate with replacement if replacement is set and pattern is set', function () { - config.url_obfuscation.enabled = true - config.url_obfuscation.regex.pattern = '/foo/[0-9]+/bar/[0-9]+/baz/[0-9]+' - config.url_obfuscation.regex.replacement = '/***' - expect(urltils.obfuscatePath(config, path)).equal('/***') - }) + t.equal(urltils.obfuscatePath(config, path), path) + t.end() + }) + + t.test( + 'should obfuscate with empty string `` if replacement is not set and pattern is set', + function (t) { + const { urltils, config, path } = t.context + config.url_obfuscation.enabled = true + config.url_obfuscation.regex.pattern = '/foo/[0-9]+/bar/[0-9]+/baz/[0-9]+' + t.equal(urltils.obfuscatePath(config, path), '') + t.end() + } + ) + + t.test( + 'should obfuscate with replacement if replacement is set and pattern is set', + function (t) { + const { urltils, config, path } = t.context + config.url_obfuscation.enabled = true + config.url_obfuscation.regex.pattern = '/foo/[0-9]+/bar/[0-9]+/baz/[0-9]+' + config.url_obfuscation.regex.replacement = '/***' + t.equal(urltils.obfuscatePath(config, path), '/***') + t.end() + } + ) - it('should obfuscate as expected with capture groups pattern over strings', function () { + t.test('should obfuscate as expected with capture groups pattern over strings', function (t) { + const { urltils, config, path } = t.context config.url_obfuscation.enabled = true config.url_obfuscation.regex.pattern = '(/foo/)(.*)(/bar/)(.*)(/baz/)(.*)' config.url_obfuscation.regex.replacement = '$1***$3***$5***' - expect(urltils.obfuscatePath(config, path)).equal('/foo/***/bar/***/baz/***') + t.equal(urltils.obfuscatePath(config, path), '/foo/***/bar/***/baz/***') + t.end() }) - it('should obfuscate as expected with regex patterns and flags', function () { + t.test('should obfuscate as expected with regex patterns and flags', function (t) { + const { urltils, config, path } = t.context config.url_obfuscation.enabled = true config.url_obfuscation.regex.pattern = '[0-9]+' config.url_obfuscation.regex.flags = 'g' config.url_obfuscation.regex.replacement = '***' - expect(urltils.obfuscatePath(config, path)).equal('/foo/***/bar/***/baz/***') + t.equal(urltils.obfuscatePath(config, path), '/foo/***/bar/***/baz/***') + t.end() }) - it('should call logger warn if obfuscation is enabled but pattern is invalid', function () { - config.url_obfuscation.enabled = true - config.url_obfuscation.regex.pattern = '[0-9+' + t.test( + 'should call logger warn if obfuscation is enabled but pattern is invalid', + function (t) { + const { urltils, config, path } = t.context + config.url_obfuscation.enabled = true + config.url_obfuscation.regex.pattern = '[0-9+' - urltils.obfuscatePath(config, path) + urltils.obfuscatePath(config, path) - expect(loggerStub.warn.calledOnce).to.be.true - }) + t.equal(t.context.loggerStub.warn.calledOnce, true) + t.end() + } + ) }) }) diff --git a/test/unit/util/code-level-metrics.test.js b/test/unit/util/code-level-metrics.test.js index 199744d557..e6985c2f45 100644 --- a/test/unit/util/code-level-metrics.test.js +++ b/test/unit/util/code-level-metrics.test.js @@ -12,14 +12,7 @@ const path = require('path') const helperPath = path.resolve(`${__dirname}/../../lib/clm-helper.js`) const sinon = require('sinon') const symbols = require('../../../lib/symbols') -tap.Test.prototype.addAssert('clmAttrs', 2, function clmAttrs(segmentStub, expectedAttrs) { - const attrs = segmentStub.addAttribute.args - const attrsObj = attrs.reduce((obj, [key, value]) => { - obj[key] = value - return obj - }, {}) - this.same(attrsObj, expectedAttrs, 'CLM attrs should match') -}) +require('../../lib/custom-assertions') /** * Helper to generate a long string @@ -46,10 +39,10 @@ tap.test('CLM Meta', (t) => { function testFunction() {} testFunction[symbols.clm] = true addCLMAttributes(testFunction, segmentStub) - t.clmAttrs(segmentStub, { + t.exactClmAttrs(segmentStub, { 'code.filepath': __filename, 'code.function': 'testFunction', - 'code.lineno': 46, + 'code.lineno': 39, 'code.column': 26 }) t.end() @@ -59,10 +52,10 @@ tap.test('CLM Meta', (t) => { const testFunction = function () {} testFunction[symbols.clm] = true addCLMAttributes(testFunction, segmentStub) - t.clmAttrs(segmentStub, { + t.exactClmAttrs(segmentStub, { 'code.filepath': __filename, 'code.function': 'testFunction', - 'code.lineno': 59, + 'code.lineno': 52, 'code.column': 35 }) t.end() @@ -73,7 +66,7 @@ tap.test('CLM Meta', (t) => { (t) => { named[symbols.clm] = true addCLMAttributes(named, segmentStub) - t.clmAttrs(segmentStub, { + t.exactClmAttrs(segmentStub, { 'code.filepath': helperPath, 'code.function': 'testFunction', 'code.lineno': 11, @@ -86,7 +79,7 @@ tap.test('CLM Meta', (t) => { t.test('should return (anonymous) as code.function from (anonymous) function reference', (t) => { anon[symbols.clm] = true addCLMAttributes(anon, segmentStub) - t.clmAttrs(segmentStub, { + t.exactClmAttrs(segmentStub, { 'code.filepath': helperPath, 'code.function': '(anonymous)', 'code.lineno': 9, @@ -98,7 +91,7 @@ tap.test('CLM Meta', (t) => { t.test('should return (anonymous) as code.function from arrow function reference', (t) => { arrow[symbols.clm] = true addCLMAttributes(arrow, segmentStub) - t.clmAttrs(segmentStub, { + t.exactClmAttrs(segmentStub, { 'code.filepath': helperPath, 'code.function': '(anonymous)', 'code.lineno': 10, diff --git a/test/versioned/amqplib/amqp-utils.js b/test/versioned/amqplib/amqp-utils.js index 0a7d600045..e821dc86bc 100644 --- a/test/versioned/amqplib/amqp-utils.js +++ b/test/versioned/amqplib/amqp-utils.js @@ -52,18 +52,14 @@ function verifySubscribe(t, tx, exchange, routingKey) { ] } - t.doesNotThrow(function () { - metrics.assertSegments(tx.trace.root, segments) - }, 'should have expected segments') + t.assertSegments(tx.trace.root, segments) - t.doesNotThrow(function () { - metrics.assertMetrics( - tx.metrics, - [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchange }]], - false, - false - ) - }, 'should have expected metrics') + t.assertMetrics( + tx.metrics, + [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchange }]], + false, + false + ) t.notMatch(tx.getFullName(), /^OtherTransaction\/Message/, 'should not set transaction name') @@ -112,7 +108,7 @@ function verifyDistributedTrace(t, produceTransaction, consumeTransaction) { function verifyConsumeTransaction(t, tx, exchange, queue, routingKey) { t.doesNotThrow(function () { - metrics.assertMetrics( + t.assertMetrics( tx.metrics, [ [{ name: 'OtherTransaction/Message/RabbitMQ/Exchange/Named/' + exchange }], @@ -148,18 +144,14 @@ function verifyConsumeTransaction(t, tx, exchange, queue, routingKey) { } function verifySendToQueue(t, tx) { - t.doesNotThrow(function () { - metrics.assertSegments(tx.trace.root, ['MessageBroker/RabbitMQ/Exchange/Produce/Named/Default']) - }, 'should have expected segments') + t.assertSegments(tx.trace.root, ['MessageBroker/RabbitMQ/Exchange/Produce/Named/Default']) - t.doesNotThrow(function () { - metrics.assertMetrics( - tx.metrics, - [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/Default' }]], - false, - false - ) - }, 'should have expected metrics') + t.assertMetrics( + tx.metrics, + [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/Default' }]], + false, + false + ) const segment = metrics.findSegment( tx.trace.root, @@ -214,18 +206,14 @@ function verifyProduce(t, tx, exchangeName, routingKey) { ] } - t.doesNotThrow(() => { - metrics.assertSegments(tx.trace.root, segments, 'should have expected segments') - }) + t.assertSegments(tx.trace.root, segments, 'should have expected segments') - t.doesNotThrow(function () { - metrics.assertMetrics( - tx.metrics, - [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchangeName }]], - false, - false - ) - }, 'should have expected metrics') + t.assertMetrics( + tx.metrics, + [[{ name: 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchangeName }]], + false, + false + ) const segment = metrics.findSegment( tx.trace.root, @@ -244,24 +232,11 @@ function verifyGet(t, tx, exchangeName, routingKey, queue) { const produceName = 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchangeName const consumeName = 'MessageBroker/RabbitMQ/Exchange/Consume/Named/' + queue if (isCallback) { - t.doesNotThrow(assertions, 'should have expected segments') - - function assertions() { - metrics.assertSegments(tx.trace.root, [produceName, consumeName, ['Callback: ']]) - } + t.assertSegments(tx.trace.root, [produceName, consumeName, ['Callback: ']]) } else { - t.doesNotThrow(function () { - metrics.assertSegments(tx.trace.root, [produceName, consumeName]) - }, 'should have expected segments') + t.assertSegments(tx.trace.root, [produceName, consumeName]) } - t.doesNotThrow(function () { - metrics.assertMetrics( - tx.metrics, - [[{ name: produceName }], [{ name: consumeName }]], - false, - false - ) - }, 'should have expected metrics') + t.assertMetrics(tx.metrics, [[{ name: produceName }], [{ name: consumeName }]], false, false) } function verifyPurge(t, tx) { @@ -304,18 +279,9 @@ function verifyPurge(t, tx) { ] } - t.doesNotThrow(() => { - metrics.assertSegments(tx.trace.root, segments, 'should have expected segments') - }) + t.assertSegments(tx.trace.root, segments, 'should have expected segments') - t.doesNotThrow(function () { - metrics.assertMetrics( - tx.metrics, - [[{ name: 'MessageBroker/RabbitMQ/Queue/Purge/Temp' }]], - false, - false - ) - }, 'should have expected metrics') + t.assertMetrics(tx.metrics, [[{ name: 'MessageBroker/RabbitMQ/Queue/Purge/Temp' }]], false, false) } function verifyTransaction(t, tx, msg) { diff --git a/test/versioned/bunyan/bunyan.tap.js b/test/versioned/bunyan/bunyan.tap.js index 811cfe8787..4c37f33106 100644 --- a/test/versioned/bunyan/bunyan.tap.js +++ b/test/versioned/bunyan/bunyan.tap.js @@ -7,12 +7,10 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const { validateLogLine } = require('../../lib/logging-helper') +require('../../lib/logging-helper') const { LOGGING } = require('../../../lib/metrics/names') const { makeSink, logStuff, originalMsgAssertion, logForwardingMsgAssertion } = require('./helpers') -tap.Test.prototype.addAssert('validateAnnotations', 2, validateLogLine) - tap.test('bunyan instrumentation', (t) => { t.autoend() diff --git a/test/versioned/express-esm/segments.tap.mjs b/test/versioned/express-esm/segments.tap.mjs index 862af325dd..5dc9c95e7c 100644 --- a/test/versioned/express-esm/segments.tap.mjs +++ b/test/versioned/express-esm/segments.tap.mjs @@ -5,7 +5,7 @@ import helper from '../../lib/agent_helper.js' import NAMES from '../../../lib/metrics/names.js' -import { assertMetrics, assertSegments } from '../../lib/metrics_helper.js' +import '../../lib/metrics_helper.js' import { test } from 'tap' import expressHelpers from './helpers.mjs' const { setup, makeRequestAndFinishTransaction } = expressHelpers @@ -33,8 +33,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -69,8 +68,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -92,8 +90,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -140,8 +137,7 @@ test('transaction segments tests', (t) => { ) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -169,8 +165,7 @@ test('transaction segments tests', (t) => { app.use('/router1', router) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/router1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -205,8 +200,7 @@ test('transaction segments tests', (t) => { app.use('/', router2) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -232,8 +226,7 @@ test('transaction segments tests', (t) => { app.get('*', router1) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -266,8 +259,7 @@ test('transaction segments tests', (t) => { app.use('/router1', router) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/router1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -297,8 +289,7 @@ test('transaction segments tests', (t) => { app.use('/subapp1', subapp) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/subapp1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -346,8 +337,7 @@ test('transaction segments tests', (t) => { app.use('/subapp1', subapp) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/subapp1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -388,8 +378,7 @@ test('transaction segments tests', (t) => { app.use('/subapp1', subapp) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/subapp1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -433,8 +422,7 @@ test('transaction segments tests', (t) => { t, endpoint: '/router1/subapp1/test' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -473,8 +461,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -499,8 +486,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -541,8 +527,7 @@ test('transaction segments tests', (t) => { const endpoint = '/router/test' const { rootSegment, transaction } = await runTest({ app, t, endpoint }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -588,8 +573,7 @@ test('transaction segments tests', (t) => { const endpoint = '/router1/router2/test' const { rootSegment, transaction } = await runTest({ app, t, endpoint }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -635,8 +619,7 @@ test('transaction segments tests', (t) => { const endpoint = '/router/test' const { rootSegment, transaction } = await runTest({ app, t, endpoint }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -679,8 +662,7 @@ test('transaction segments tests', (t) => { const endpoint = '/router1/router2/test' const { rootSegment, transaction } = await runTest({ app, t, endpoint }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -714,8 +696,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/a/b' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -742,8 +723,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/abcd' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -765,8 +745,7 @@ test('transaction segments tests', (t) => { }) const { rootSegment, transaction } = await runTest({ app, t, endpoint: '/a' }) - checkSegments( - t, + t.assertSegments( rootSegment, [ NAMES.EXPRESS.MIDDLEWARE + 'query', @@ -789,12 +768,6 @@ test('transaction segments tests', (t) => { } }) -function checkSegments(t, segments, expected, opts) { - t.doesNotThrow(function () { - assertSegments(segments, expected, opts) - }, 'should have expected segments') -} - function checkMetrics(t, metrics, expected, path) { if (path === undefined) { path = '/test' @@ -826,5 +799,5 @@ function checkMetrics(t, metrics, expected, path) { expectedAll.push([{ name: metric, scope: 'WebTransaction/Expressjs/GET/' + path }]) } - assertMetrics(metrics, expectedAll, true, false) + t.assertMetrics(metrics, expectedAll, true, false) } diff --git a/test/versioned/express/client-disconnect.tap.js b/test/versioned/express/client-disconnect.tap.js index 1d4f2f8e5b..78b337ddad 100644 --- a/test/versioned/express/client-disconnect.tap.js +++ b/test/versioned/express/client-disconnect.tap.js @@ -7,7 +7,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const metrics = require('../../lib/metrics_helper') +require('../../lib/metrics_helper') const http = require('http') function generateApp(t) { @@ -46,22 +46,20 @@ tap.test('Client Premature Disconnection', (t) => { }) agent.on('transactionFinished', (transaction) => { - t.doesNotThrow(function () { - metrics.assertSegments( - transaction.trace.root, + t.assertSegments( + transaction.trace.root, + [ + 'WebTransaction/Expressjs/POST//test', [ - 'WebTransaction/Expressjs/POST//test', - [ - 'Nodejs/Middleware/Expressjs/query', - 'Nodejs/Middleware/Expressjs/expressInit', - 'Nodejs/Middleware/Expressjs/jsonParser', - 'Expressjs/Route Path: /test', - ['Nodejs/Middleware/Expressjs/controller', ['timers.setTimeout']] - ] - ], - { exact: true } - ) - }, 'should have expected segments') + 'Nodejs/Middleware/Expressjs/query', + 'Nodejs/Middleware/Expressjs/expressInit', + 'Nodejs/Middleware/Expressjs/jsonParser', + 'Expressjs/Route Path: /test', + ['Nodejs/Middleware/Expressjs/controller', ['timers.setTimeout']] + ] + ], + { exact: true } + ) t.equal(agent.getTransaction(), null, 'should have ended the transaction') t.end() diff --git a/test/versioned/express/segments.tap.js b/test/versioned/express/segments.tap.js index 97cb3124b7..10e578036b 100644 --- a/test/versioned/express/segments.tap.js +++ b/test/versioned/express/segments.tap.js @@ -8,13 +8,10 @@ const helper = require('../../lib/agent_helper') const http = require('http') const NAMES = require('../../../lib/metrics/names') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics -const assertSegments = require('../../lib/metrics_helper').assertSegments +require('../../lib/metrics_helper') const tap = require('tap') const { test } = tap -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) - let express let agent let app @@ -981,9 +978,7 @@ function makeRequest(server, path, callback) { } function checkSegments(t, segments, expected, opts) { - t.doesNotThrow(function () { - assertSegments(segments, expected, opts) - }, 'should have expected segments') + t.assertSegments(segments, expected, opts) } function checkMetrics(t, metrics, expected, path) { @@ -1017,5 +1012,5 @@ function checkMetrics(t, metrics, expected, path) { expectedAll.push([{ name: metric, scope: 'WebTransaction/Expressjs/GET/' + path }]) } - assertMetrics(metrics, expectedAll, true, false) + t.assertMetrics(metrics, expectedAll, true, false) } diff --git a/test/versioned/fastify/add-hook.tap.js b/test/versioned/fastify/add-hook.tap.js index cfcbc3292d..c40182c03b 100644 --- a/test/versioned/fastify/add-hook.tap.js +++ b/test/versioned/fastify/add-hook.tap.js @@ -7,7 +7,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const metrics = require('../../lib/metrics_helper') +require('../../lib/metrics_helper') const common = require('./common') // all of these events fire before the route handler @@ -105,7 +105,7 @@ tap.test('fastify hook instrumentation', (t) => { ] ] } - metrics.assertSegments(transaction.trace.root, expectedSegments) + t.assertSegments(transaction.trace.root, expectedSegments) }) await fastify.listen(0) @@ -161,7 +161,7 @@ tap.test('fastify hook instrumentation', (t) => { ] } - metrics.assertSegments(transaction.trace.root, expectedSegments) + t.assertSegments(transaction.trace.root, expectedSegments) }) await fastify.listen(0) diff --git a/test/versioned/fastify/code-level-metrics-hooks.tap.js b/test/versioned/fastify/code-level-metrics-hooks.tap.js index 4465123077..3349736812 100644 --- a/test/versioned/fastify/code-level-metrics-hooks.tap.js +++ b/test/versioned/fastify/code-level-metrics-hooks.tap.js @@ -9,8 +9,6 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') const common = require('./common') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) - function setupFastifyServer(fastify) { common.setupRoutes(fastify) } diff --git a/test/versioned/fastify/code-level-metrics-middleware.tap.js b/test/versioned/fastify/code-level-metrics-middleware.tap.js index 1ebcaa3340..fcfcceeb0c 100644 --- a/test/versioned/fastify/code-level-metrics-middleware.tap.js +++ b/test/versioned/fastify/code-level-metrics-middleware.tap.js @@ -11,8 +11,6 @@ const common = require('./common') const semver = require('semver') const { version: pkgVersion } = require('fastify/package') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) - async function setupFastifyServer(fastify, calls) { common.setupRoutes(fastify) await fastify.register(require('middie')) diff --git a/test/versioned/fastify/naming-common.js b/test/versioned/fastify/naming-common.js index e2adb09318..79f9c9875c 100644 --- a/test/versioned/fastify/naming-common.js +++ b/test/versioned/fastify/naming-common.js @@ -5,7 +5,7 @@ 'use strict' const { routesToTest, makeRequest } = require('./common') -const metrics = require('../../lib/metrics_helper') +require('../../lib/metrics_helper') const helper = require('../../lib/agent_helper') module.exports = function createTests(t, getExpectedSegments) { @@ -36,7 +36,7 @@ module.exports = function createTests(t, getExpectedSegments) { ] } - metrics.assertSegments(transaction.trace.root, expectedSegments) + t.assertSegments(transaction.trace.root, expectedSegments) }) await fastify.listen(0) diff --git a/test/versioned/grpc/util.cjs b/test/versioned/grpc/util.cjs index b4d5a518b4..9da0391b63 100644 --- a/test/versioned/grpc/util.cjs +++ b/test/versioned/grpc/util.cjs @@ -6,7 +6,6 @@ 'use strict' const util = module.exports const metricsHelpers = require('../../lib/metrics_helper') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics const protoLoader = require('@grpc/proto-loader') const serverImpl = require('./grpc-server.cjs') const DESTINATIONS = require('../../../lib/config/attribute-filter').DESTINATIONS @@ -143,7 +142,7 @@ util.assertExternalSegment = function assertExternalSegment({ }) { const methodName = util.getRPCName(fnName) const segmentName = `${EXTERNAL.PREFIX}${CLIENT_ADDR}:${port}${methodName}` - metricsHelpers.assertSegments(tx.trace.root, [segmentName], { exact: false }) + t.assertSegments(tx.trace.root, [segmentName], { exact: false }) const segment = metricsHelpers.findSegment(tx.trace.root, segmentName) const attributes = segment.getAttributes() t.equal( @@ -164,7 +163,7 @@ util.assertExternalSegment = function assertExternalSegment({ ) t.equal(attributes.component, 'gRPC', 'should have the component set to "gRPC"') const expectedMetrics = buildExpectedMetrics(port) - metricsHelpers.assertMetrics(tx.metrics, [expectedMetrics], false, false) + t.assertMetrics(tx.metrics, [expectedMetrics], false, false) } /** @@ -214,9 +213,7 @@ util.assertServerMetrics = function assertServerMetrics({ t, agentMetrics, fnNam [{ name: `Apdex/WebFrameworkUri/gRPC//helloworld.Greeter/${fnName}` }], [{ name: 'Apdex' }] ] - t.doesNotThrow(() => { - assertMetrics(agentMetrics, expectedServerMetrics, false, false) - }, 'should have the expected metrics') + t.assertMetrics(agentMetrics, expectedServerMetrics, false, false) } util.assertDistributedTracing = function assertDistributedTracing({ diff --git a/test/versioned/hapi/segments.tap.js b/test/versioned/hapi/segments.tap.js index 06532b8efb..90d745f893 100644 --- a/test/versioned/hapi/segments.tap.js +++ b/test/versioned/hapi/segments.tap.js @@ -8,11 +8,9 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') const http = require('http') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics -const assertSegments = require('../../lib/metrics_helper').assertSegments +require('../../lib/metrics_helper') const NAMES = require('../../../lib/metrics/names') const utils = require('./hapi-utils') -tap.Test.prototype.addAssert('clmAttrs', 1, helper.assertCLMAttrs) let agent let server @@ -43,7 +41,7 @@ tap.test('Hapi segments', function (t) { runTest(t, function (segments, transaction) { checkMetrics(t, transaction.metrics, [NAMES.HAPI.MIDDLEWARE + 'myHandler//test']) - checkSegments(t, transaction.trace.root.children[0], [ + t.assertSegments(transaction.trace.root.children[0], [ NAMES.HAPI.MIDDLEWARE + 'myHandler//test' ]) t.end() @@ -65,7 +63,7 @@ tap.test('Hapi segments', function (t) { runTest(t, function (segments, transaction) { checkMetrics(t, transaction.metrics, [NAMES.HAPI.MIDDLEWARE + 'customHandler//test']) - checkSegments(t, transaction.trace.root.children[0], [ + t.assertSegments(transaction.trace.root.children[0], [ NAMES.HAPI.MIDDLEWARE + 'customHandler//test' ]) t.end() @@ -90,7 +88,7 @@ tap.test('Hapi segments', function (t) { NAMES.HAPI.MIDDLEWARE + '//onRequest', NAMES.HAPI.MIDDLEWARE + 'myHandler//test' ]) - checkSegments(t, transaction.trace.root.children[0], [ + t.assertSegments(transaction.trace.root.children[0], [ NAMES.HAPI.MIDDLEWARE + '//onRequest', NAMES.HAPI.MIDDLEWARE + 'myHandler//test' ]) @@ -120,7 +118,7 @@ tap.test('Hapi segments', function (t) { NAMES.HAPI.MIDDLEWARE + '//onRequest', NAMES.HAPI.MIDDLEWARE + 'customHandler//test' ]) - checkSegments(t, transaction.trace.root.children[0], [ + t.assertSegments(transaction.trace.root.children[0], [ NAMES.HAPI.MIDDLEWARE + '//onRequest', NAMES.HAPI.MIDDLEWARE + 'customHandler//test' ]) @@ -286,11 +284,5 @@ function checkMetrics(t, metrics, expected, path) { expectedAll.push([{ name: metric, scope: 'WebTransaction/Hapi/GET/' + path }]) } - assertMetrics(metrics, expectedAll, true, false) -} - -function checkSegments(t, segments, expected, opts) { - t.doesNotThrow(function () { - assertSegments(segments, expected, opts) - }, 'should have expected segments') + t.assertMetrics(metrics, expectedAll, true, false) } diff --git a/test/versioned/ioredis/ioredis-3.tap.js b/test/versioned/ioredis/ioredis-3.tap.js index bf213c4d35..932fe33ea7 100644 --- a/test/versioned/ioredis/ioredis-3.tap.js +++ b/test/versioned/ioredis/ioredis-3.tap.js @@ -7,7 +7,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics +require('../../lib/metrics_helper') const params = require('../../lib/params') // Indicates unique database in Redis. 0-15 supported. @@ -39,7 +39,7 @@ tap.test('ioredis instrumentation', function (t) { [{ name: 'Datastore/Redis/all' }], [{ name: 'Datastore/operation/Redis/set' }] ] - assertMetrics(tx.metrics, expected, false, false) + t.assertMetrics(tx.metrics, expected, false, false) t.end() }) diff --git a/test/versioned/ioredis/ioredis.tap.js b/test/versioned/ioredis/ioredis.tap.js index 026d8d9501..0ed9c7b727 100644 --- a/test/versioned/ioredis/ioredis.tap.js +++ b/test/versioned/ioredis/ioredis.tap.js @@ -7,7 +7,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const assertMetrics = require('../../lib/metrics_helper').assertMetrics +require('../../lib/metrics_helper') const params = require('../../lib/params') // Indicates unique database in Redis. 0-15 supported. @@ -41,8 +41,7 @@ tap.test('ioredis instrumentation', (t) => { }) t.test('creates expected metrics', { timeout: 5000 }, (t) => { - t.plan(1) - + t.plan(6) agent.on('transactionFinished', function (tx) { const expected = [ [{ name: 'Datastore/all' }], @@ -50,9 +49,7 @@ tap.test('ioredis instrumentation', (t) => { [{ name: 'Datastore/operation/Redis/set' }] ] - t.doesNotThrow(() => { - assertMetrics(tx.metrics, expected, false, false) - }, 'should have expected metrics') + t.assertMetrics(tx.metrics, expected, false, false) t.end() }) diff --git a/test/versioned/openai/chat-completions.tap.js b/test/versioned/openai/chat-completions.tap.js index 2fe984ca7b..db9335ffca 100644 --- a/test/versioned/openai/chat-completions.tap.js +++ b/test/versioned/openai/chat-completions.tap.js @@ -12,18 +12,13 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const { assertSegments } = require('../../lib/metrics_helper') +// load the assertSegments assertion +require('../../lib/metrics_helper') const { AI: { OPENAI } } = require('../../../lib/metrics/names') const responses = require('./mock-responses') -const { - beforeHook, - afterEachHook, - afterHook, - assertChatCompletionMessages, - assertChatCompletionSummary -} = require('./common') +const { beforeHook, afterEachHook, afterHook } = require('./common') const semver = require('semver') const fs = require('fs') // have to read and not require because openai does not export the package.json @@ -31,9 +26,6 @@ const { version: pkgVersion } = JSON.parse( fs.readFileSync(`${__dirname}/node_modules/openai/package.json`) ) -tap.Test.prototype.addAssert('llmMessages', 1, assertChatCompletionMessages) -tap.Test.prototype.addAssert('llmSummary', 1, assertChatCompletionSummary) - tap.test('OpenAI instrumentation - chat completions', (t) => { t.autoend() @@ -55,7 +47,7 @@ tap.test('OpenAI instrumentation - chat completions', (t) => { test.equal(results.choices[0].message.content, '1 plus 2 is 3.') test.doesNotThrow(() => { - assertSegments( + test.assertSegments( tx.trace.root, [OPENAI.COMPLETION, [`External/${host}:${port}/chat/completions`]], { exact: false } @@ -138,7 +130,7 @@ tap.test('OpenAI instrumentation - chat completions', (t) => { test.equal(chunk.choices[0].message.content, res) test.doesNotThrow(() => { - assertSegments( + test.assertSegments( tx.trace.root, [OPENAI.COMPLETION, [`External/${host}:${port}/chat/completions`]], { exact: false } @@ -265,7 +257,7 @@ tap.test('OpenAI instrumentation - chat completions', (t) => { t.equal(events.length, 0) // we will still record the external segment but not the chat completion test.doesNotThrow(() => { - assertSegments(tx.trace.root, [ + test.assertSegments(tx.trace.root, [ 'timers.setTimeout', `External/${host}:${port}/chat/completions` ]) diff --git a/test/versioned/openai/common.js b/test/versioned/openai/common.js index 86fc70f6c6..ffe1f21dbc 100644 --- a/test/versioned/openai/common.js +++ b/test/versioned/openai/common.js @@ -4,6 +4,7 @@ */ 'use strict' +const tap = require('tap') const common = module.exports const createOpenAIMockServer = require('./mock-server') const helper = require('../../lib/agent_helper') @@ -35,14 +36,7 @@ common.afterHook = function afterHook(t) { t.context.agent && helper.unloadAgent(t.context.agent) } -common.assertChatCompletionMessages = function assertChatCompletionMessages({ - tx, - chatMsgs, - id, - model, - reqContent, - resContent -}) { +function assertChatCompletionMessages({ tx, chatMsgs, id, model, reqContent, resContent }) { const baseMsg = { 'appName': 'New Relic for Node.js tests', 'request_id': '49dbbffbd3c3f4612aa48def69059aad', @@ -80,13 +74,7 @@ common.assertChatCompletionMessages = function assertChatCompletionMessages({ }) } -common.assertChatCompletionSummary = function assertChatCompletionSummary({ - tx, - model, - chatSummary, - tokenUsage, - error = false -}) { +function assertChatCompletionSummary({ tx, model, chatSummary, tokenUsage, error = false }) { let expectedChatSummary = { 'id': /[a-f0-9]{36}/, 'appName': 'New Relic for Node.js tests', @@ -124,3 +112,6 @@ common.assertChatCompletionSummary = function assertChatCompletionSummary({ this.equal(chatSummary[0].type, 'LlmChatCompletionSummary') this.match(chatSummary[1], expectedChatSummary, 'should match chat summary message') } + +tap.Test.prototype.addAssert('llmMessages', 1, assertChatCompletionMessages) +tap.Test.prototype.addAssert('llmSummary', 1, assertChatCompletionSummary) diff --git a/test/versioned/openai/embeddings.tap.js b/test/versioned/openai/embeddings.tap.js index db6a101b5c..7440ffaed5 100644 --- a/test/versioned/openai/embeddings.tap.js +++ b/test/versioned/openai/embeddings.tap.js @@ -12,7 +12,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') -const { assertSegments } = require('../../lib/metrics_helper') +require('../../lib/metrics_helper') const { beforeHook, afterEachHook, afterHook } = require('./common') const { AI: { OPENAI } @@ -45,11 +45,9 @@ tap.test('OpenAI instrumentation - embedding', (t) => { test.notOk(results.api_key, 'should remove api_key from user result') test.equal(results.model, 'text-embedding-ada-002-v2') - test.doesNotThrow(() => { - assertSegments(tx.trace.root, [OPENAI.EMBEDDING, [`External/${host}:${port}/embeddings`]], { - exact: false - }) - }, 'should have expected segments') + t.assertSegments(tx.trace.root, [OPENAI.EMBEDDING, [`External/${host}:${port}/embeddings`]], { + exact: false + }) tx.end() test.end() }) diff --git a/test/versioned/pino/pino.tap.js b/test/versioned/pino/pino.tap.js index 5a45717edd..20cfe829da 100644 --- a/test/versioned/pino/pino.tap.js +++ b/test/versioned/pino/pino.tap.js @@ -14,37 +14,14 @@ const { LOGGING } = require('../../../lib/metrics/names') const { originalMsgAssertion } = require('./helpers') const semver = require('semver') const { version: pinoVersion } = require('pino/package') - -tap.Test.prototype.addAssert( - 'validateNrLogLine', - 2, - function validateNrLogLine({ line: logLine, message, level, config }) { - this.equal( - logLine['entity.name'], - config.applications()[0], - 'should have entity name that matches app' - ) - this.equal(logLine['entity.guid'], 'pino-guid', 'should have set entity guid') - this.equal(logLine['entity.type'], 'SERVICE', 'should have entity type of SERVICE') - this.equal(logLine.hostname, config.getHostnameSafe(), 'should have proper hostname') - this.match(logLine.timestamp, /[\d]{10}/, 'should have proper unix timestamp') - this.notOk(logLine.message.includes('NR-LINKING'), 'should not contain NR-LINKING metadata') - if (message) { - this.equal(logLine.message, message, 'message should be the same as log') - } - - if (level) { - this.equal(logLine.level, level, 'level should be string value not number') - } - } -) +require('../../lib/logging-helper') tap.test('Pino instrumentation', (t) => { t.autoend() function setupAgent(context, config) { context.agent = helper.instrumentMockedAgent(config) - context.agent.config.entity_guid = 'pino-guid' + context.agent.config.entity_guid = 'test-guid' context.pino = require('pino') context.stream = sink() context.logger = context.pino({ level: 'debug' }, context.stream) @@ -147,7 +124,7 @@ tap.test('Pino instrumentation', (t) => { }) t.equal(agent.logs.getEvents().length, 1, 'should have 1 log in aggregator') const formattedLine = agent.logs.getEvents()[0]() - t.validateNrLogLine({ line: formattedLine, message, level, config }) + t.validateAnnotations({ line: formattedLine, message, level, config }) t.end() }) @@ -167,7 +144,7 @@ tap.test('Pino instrumentation', (t) => { // See: https://github.com/pinojs/pino/pull/1779/files if (semver.gte(pinoVersion, '8.15.1')) { const formattedLine = agent.logs.getEvents()[0]() - t.validateNrLogLine({ line: formattedLine, message: testMsg, level, config }) + t.validateAnnotations({ line: formattedLine, message: testMsg, level, config }) } else { t.notOk(agent.logs.getEvents()[0](), 'should not return a log line if invalid') t.notOk(agent.logs._toPayloadSync(), 'should not send any logs') @@ -189,7 +166,7 @@ tap.test('Pino instrumentation', (t) => { }) t.equal(agent.logs.getEvents().length, 1, 'should have 1 log in aggregator') const formattedLine = agent.logs.getEvents()[0]() - t.validateNrLogLine({ + t.validateAnnotations({ line: formattedLine, message: err.message, level, @@ -228,7 +205,7 @@ tap.test('Pino instrumentation', (t) => { ) const formattedLine = agent.logs.getEvents()[0]() - t.validateNrLogLine({ line: formattedLine, message, level, config }) + t.validateAnnotations({ line: formattedLine, message, level, config }) t.equal(formattedLine['trace.id'], meta['trace.id']) t.equal(formattedLine['span.id'], meta['span.id']) t.end() @@ -249,7 +226,7 @@ tap.test('Pino instrumentation', (t) => { t.notOk(line.hostname, 'should not have hostname when overriding base chindings') t.equal(agent.logs.getEvents().length, 1, 'should have 1 log in aggregator') const formattedLine = agent.logs.getEvents()[0]() - t.validateNrLogLine({ line: formattedLine, message, level, config }) + t.validateAnnotations({ line: formattedLine, message, level, config }) t.end() } ) @@ -290,7 +267,7 @@ tap.test('Pino instrumentation', (t) => { agent.logs.getEvents().forEach((logLine, index) => { const formattedLine = logLine() - t.validateNrLogLine({ + t.validateAnnotations({ line: formattedLine, message: messages[index], level, diff --git a/test/versioned/prisma/utils.js b/test/versioned/prisma/utils.js index 0e25b4e45d..215f939f4b 100644 --- a/test/versioned/prisma/utils.js +++ b/test/versioned/prisma/utils.js @@ -5,7 +5,7 @@ 'use strict' const utils = module.exports -const { assertSegments, findSegment, getMetricHostName } = require('../../lib/metrics_helper') +const { findSegment, getMetricHostName } = require('../../lib/metrics_helper') const { DB, PRISMA } = require('../../../lib/metrics/names') const params = require('../../lib/params') const expectedUpsertMetrics = { @@ -59,7 +59,7 @@ function verifyTraces(t, agent, transaction) { t.ok(trace, 'trace should exist') t.ok(trace.root, 'root element should exist') - assertSegments(trace.root, [findMany, update, update, findMany], { exact: true }) + t.assertSegments(trace.root, [findMany, update, update, findMany], { exact: true }) const findManySegment = findSegment(trace.root, findMany) t.ok(findManySegment.timer.hrDuration, 'findMany segment should have ended') const updateSegment = findSegment(trace.root, update) diff --git a/test/versioned/restify/restify-post-7/async-handlers.tap.js b/test/versioned/restify/restify-post-7/async-handlers.tap.js index ee6729ade7..cfd3ac7ce3 100644 --- a/test/versioned/restify/restify-post-7/async-handlers.tap.js +++ b/test/versioned/restify/restify-post-7/async-handlers.tap.js @@ -8,7 +8,7 @@ const tap = require('tap') const helper = require('../../../lib/agent_helper') -const { assertMetrics } = require('../../../lib/metrics_helper') +require('../../../lib/metrics_helper') const { runTest } = require('./common') const simulateAsyncWork = async () => { @@ -172,7 +172,7 @@ tap.test('Restify metrics for async handlers', (t) => { helper.makeGetRequest(url, function (error) { t.error(error) - assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) + t.assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) t.end() }) }) diff --git a/test/versioned/restify/restify-post-7/restify.tap.js b/test/versioned/restify/restify-post-7/restify.tap.js index d1ddda1f12..84c72a8666 100644 --- a/test/versioned/restify/restify-post-7/restify.tap.js +++ b/test/versioned/restify/restify-post-7/restify.tap.js @@ -8,7 +8,7 @@ const tap = require('tap') const helper = require('../../../lib/agent_helper') -const { assertMetrics } = require('../../../lib/metrics_helper') +require('../../../lib/metrics_helper') const METRIC = 'WebTransaction/Restify/GET//hello/:name' @@ -168,7 +168,7 @@ tap.test('Restify', (t) => { helper.makeGetRequest(url, function (error) { t.error(error) - assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) + t.assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) t.end() }) }) diff --git a/test/versioned/restify/restify-pre-7/restify.tap.js b/test/versioned/restify/restify-pre-7/restify.tap.js index 5de02036b8..607786210a 100644 --- a/test/versioned/restify/restify-pre-7/restify.tap.js +++ b/test/versioned/restify/restify-pre-7/restify.tap.js @@ -7,7 +7,7 @@ const tap = require('tap') const helper = require('../../../lib/agent_helper') -const { assertMetrics } = require('../../../lib/metrics_helper') +require('../../../lib/metrics_helper') const METRIC = 'WebTransaction/Restify/GET//hello/:name' @@ -161,7 +161,7 @@ tap.test('Restify', (t) => { helper.makeGetRequest(url, function (error) { t.error(error) - assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) + t.assertMetrics(agent.metrics, expectedMiddlewareMetrics, false, false) t.end() }) }) diff --git a/test/versioned/undici/requests.tap.js b/test/versioned/undici/requests.tap.js index 9b7a04d664..8a81fb9c01 100644 --- a/test/versioned/undici/requests.tap.js +++ b/test/versioned/undici/requests.tap.js @@ -86,7 +86,7 @@ tap.test('Undici request tests', (t) => { }) t.equal(statusCode, 200) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) tx.end() t.end() }) @@ -120,7 +120,7 @@ tap.test('Undici request tests', (t) => { await client.request({ path: '/', method: 'GET' }) - metrics.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { + t.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { exact: false }) @@ -201,7 +201,7 @@ tap.test('Undici request tests', (t) => { const [{ statusCode }, { statusCode: statusCode2 }] = await Promise.all([req1, req2]) t.equal(statusCode, 200) t.equal(statusCode2, 200) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { + t.assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { exact: false }) tx.end() @@ -218,7 +218,7 @@ tap.test('Undici request tests', (t) => { }) } catch (err) { t.match(err.message, /getaddrinfo.*invalidurl/) - metrics.assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) + t.assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) t.equal(tx.exceptions.length, 1) tx.end() t.end() @@ -239,7 +239,7 @@ tap.test('Undici request tests', (t) => { }, 100) await req } catch (err) { - metrics.assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) t.equal(tx.exceptions.length, 1) t.equal(tx.exceptions[0].error.message, 'Request aborted') tx.end() @@ -266,7 +266,7 @@ tap.test('Undici request tests', (t) => { try { await req } catch (error) { - metrics.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { + t.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { exact: false }) @@ -290,7 +290,7 @@ tap.test('Undici request tests', (t) => { method: 'GET' }) t.equal(statusCode, 400) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) tx.end() t.end() }) @@ -300,7 +300,7 @@ tap.test('Undici request tests', (t) => { helper.runInTransaction(agent, async (tx) => { const res = await undici.fetch(REQUEST_URL) t.equal(res.status, 200) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/`], { exact: false }) tx.end() t.end() }) @@ -323,7 +323,7 @@ tap.test('Undici request tests', (t) => { }) } ) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) tx.end() t.end() }) @@ -359,7 +359,7 @@ tap.test('Undici request tests', (t) => { }), (err) => { t.error(err) - metrics.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) + t.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) tx.end() t.end() } diff --git a/test/versioned/winston/winston.tap.js b/test/versioned/winston/winston.tap.js index 156fca81b7..9dd5ddb630 100644 --- a/test/versioned/winston/winston.tap.js +++ b/test/versioned/winston/winston.tap.js @@ -8,7 +8,7 @@ const tap = require('tap') const helper = require('../../lib/agent_helper') const concat = require('concat-stream') -const { validateLogLine } = require('../../lib/logging-helper') +require('../../lib/logging-helper') const { Writable } = require('stream') const { LOGGING } = require('../../../lib/metrics/names') // winston puts the log line getting construct through formatters on a symbol @@ -22,8 +22,6 @@ const { logForwardingMsgAssertion } = require('./helpers') -tap.Test.prototype.addAssert('validateAnnotations', 2, validateLogLine) - tap.test('winston instrumentation', (t) => { t.autoend() diff --git a/third_party_manifest.json b/third_party_manifest.json index 8529d679fe..94a1f8c082 100644 --- a/third_party_manifest.json +++ b/third_party_manifest.json @@ -1,5 +1,5 @@ { - "lastUpdated": "Thu Dec 07 2023 10:54:24 GMT-0500 (Eastern Standard Time)", + "lastUpdated": "Mon Dec 18 2023 17:19:25 GMT-0500 (Eastern Standard Time)", "projectName": "New Relic Node Agent", "projectUrl": "https://github.com/newrelic/node-newrelic", "includeOptDeps": true, @@ -159,15 +159,15 @@ "email": "nathan@tootallnate.net", "url": "http://n8.io/" }, - "import-in-the-middle@1.4.2": { + "import-in-the-middle@1.6.0": { "name": "import-in-the-middle", - "version": "1.4.2", - "range": "^1.4.2", + "version": "1.6.0", + "range": "^1.6.0", "licenses": "Apache-2.0", "repoUrl": "https://github.com/DataDog/import-in-the-middle", - "versionedRepoUrl": "https://github.com/DataDog/import-in-the-middle/tree/v1.4.2", + "versionedRepoUrl": "https://github.com/DataDog/import-in-the-middle/tree/v1.6.0", "licenseFile": "node_modules/import-in-the-middle/LICENSE", - "licenseUrl": "https://github.com/DataDog/import-in-the-middle/blob/v1.4.2/LICENSE", + "licenseUrl": "https://github.com/DataDog/import-in-the-middle/blob/v1.6.0/LICENSE", "licenseTextSource": "file", "publisher": "Bryan English", "email": "bryan.english@datadoghq.com" @@ -363,19 +363,6 @@ "publisher": "Ben Coe", "email": "ben@npmjs.com" }, - "chai@4.3.8": { - "name": "chai", - "version": "4.3.8", - "range": "^4.1.2", - "licenses": "MIT", - "repoUrl": "https://github.com/chaijs/chai", - "versionedRepoUrl": "https://github.com/chaijs/chai/tree/v4.3.8", - "licenseFile": "node_modules/chai/LICENSE", - "licenseUrl": "https://github.com/chaijs/chai/blob/v4.3.8/LICENSE", - "licenseTextSource": "file", - "publisher": "Jake Luer", - "email": "jake@alogicalparadox.com" - }, "clean-jsdoc-theme@4.2.10": { "name": "clean-jsdoc-theme", "version": "4.2.10",