diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 666f9e9..f12ed04 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,10 +1,52 @@ name: Test on: push: + pull_request: permissions: contents: read jobs: + test-discord-bot: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + node: [16] + os: [ubuntu-20.04] + name: Discord Bot Sample App + steps: + ## Set up Stargate + - name: disable and stop mono-xsp4.service + run: | + sudo kill -9 $(sudo lsof -t -i:8084) + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + with: + repository: stargate/stargate-mongoose + path: stargate-mongoose + ref: main + - name: Set up JSON API + run: | + chmod +x bin/start_json_api.sh + bin/start_json_api.sh + working-directory: stargate-mongoose + - name: Wait for services + run: | + while ! nc -z localhost 8181; do sleep 1; done + while ! nc -z localhost 8081; do sleep 1; done + working-directory: stargate-mongoose + ## Set up and run discord bot sample app tests + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + - name: Setup node + uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3.1.1 + with: + node-version: ${{ matrix.node }} + + - run: npm install + working-directory: discord-bot + - run: npm run lint + working-directory: discord-bot + - run: npm test + working-directory: discord-bot test-ecommerce: runs-on: ${{ matrix.os }} strategy: @@ -26,11 +68,11 @@ jobs: - name: Set up JSON API run: | chmod +x bin/start_json_api.sh - bin/start_json_api.sh -t v2.0.13 -j v1.0.0-ALPHA-9 + bin/start_json_api.sh working-directory: stargate-mongoose - name: Wait for services run: | - while ! nc -z localhost 8080; do sleep 1; done + while ! nc -z localhost 8181; do sleep 1; done while ! nc -z localhost 8081; do sleep 1; done working-directory: stargate-mongoose ## Set up and run ecommerce sample app tests @@ -42,5 +84,7 @@ jobs: - run: npm install working-directory: netlify-functions-ecommerce + - run: npm run lint + working-directory: netlify-functions-ecommerce - run: npm test working-directory: netlify-functions-ecommerce \ No newline at end of file diff --git a/api-compatibility.versions b/api-compatibility.versions index 7fb5f29..7fa1f61 100644 --- a/api-compatibility.versions +++ b/api-compatibility.versions @@ -1,2 +1,2 @@ -stargate_version=v2.1.0-ALPHA-6 -json_api_version=v1.0.0-ALPHA-12 \ No newline at end of file +stargate_version=v2.1.0-BETA-2 +json_api_version=v1.0.0-BETA-3 diff --git a/discord-bot/.env.test b/discord-bot/.env.test new file mode 100644 index 0000000..8990b89 --- /dev/null +++ b/discord-bot/.env.test @@ -0,0 +1,4 @@ +JSON_API_URL=http://127.0.0.1:8181/v1/discordbot_test +JSON_API_AUTH_USERNAME=cassandra +JSON_API_AUTH_PASSWORD=cassandra +JSON_API_AUTH_URL=http://localhost:8081/v1/auth \ No newline at end of file diff --git a/discord-bot/index.js b/discord-bot/index.js index 6af01be..426a6e8 100644 --- a/discord-bot/index.js +++ b/discord-bot/index.js @@ -69,7 +69,6 @@ async function run() { authUrl: process.env.JSON_API_AUTH_URL }; } - await mongoose.connect(uri, jsonApiConnectOptions); console.log('Connecting to', uri); await mongoose.connect(uri, jsonApiConnectOptions); // Login to Discord with your client's token diff --git a/discord-bot/package.json b/discord-bot/package.json index a2d2353..3753ff7 100644 --- a/discord-bot/package.json +++ b/discord-bot/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "lint": "eslint .", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "mocha test/*.js", "start": "node ./index.js" }, "author": "", @@ -16,11 +16,14 @@ "dependencies": { "@discordjs/rest": "2.2.0", "@masteringjs/eslint-config": "0.0.1", - "@sapphire/framework": "4.8.2", "discord.js": "14.14.1", "dotenv": "16.3.1", "eslint": "8.54.0", "mongoose": "7.x", "stargate-mongoose": "0.3.0" + }, + "devDependencies": { + "mocha": "10.2.0", + "sinon": "17.0.1" } } diff --git a/discord-bot/test/countdocuments.test.js b/discord-bot/test/countdocuments.test.js new file mode 100644 index 0000000..50d563a --- /dev/null +++ b/discord-bot/test/countdocuments.test.js @@ -0,0 +1,21 @@ +'use strict'; + +const Bot = require('../models/bot'); +const assert = require('assert'); +const { describe, it } = require('mocha'); +const countdocuments = require('../commands/countdocuments'); +const sinon = require('sinon'); + +describe('countdocuments', function() { + it('returns the number of bot documents', async function() { + await Bot.deleteMany({}); + await Bot.create({ name: 'test' }); + + const interaction = { + reply: sinon.stub() + }; + await countdocuments.execute(interaction); + assert.ok(interaction.reply.calledOnce); + assert.deepEqual(interaction.reply.getCalls()[0].args, [1]); + }); +}); \ No newline at end of file diff --git a/discord-bot/test/createdocument.test.js b/discord-bot/test/createdocument.test.js new file mode 100644 index 0000000..0f6d0d4 --- /dev/null +++ b/discord-bot/test/createdocument.test.js @@ -0,0 +1,24 @@ +'use strict'; + +const Bot = require('../models/bot'); +const assert = require('assert'); +const { describe, it } = require('mocha'); +const createdocument = require('../commands/createdocument'); +const sinon = require('sinon'); + +describe('createdocument', function() { + it('inserts a new document', async function() { + await Bot.deleteMany({}); + + const interaction = { + reply: sinon.stub() + }; + await createdocument.execute(interaction); + assert.ok(interaction.reply.calledOnce); + assert.deepEqual(interaction.reply.getCalls()[0].args, ['done!']); + + const docs = await Bot.find(); + assert.equal(docs.length, 1); + assert.equal(docs[0].name, 'I am a document'); + }); +}); \ No newline at end of file diff --git a/discord-bot/test/ping.test.js b/discord-bot/test/ping.test.js new file mode 100644 index 0000000..b7d1917 --- /dev/null +++ b/discord-bot/test/ping.test.js @@ -0,0 +1,17 @@ +'use strict'; + +const assert = require('assert'); +const { describe, it } = require('mocha'); +const ping = require('../commands/ping'); +const sinon = require('sinon'); + +describe('ping', function() { + it('replies with "Pong!"', async function() { + const interaction = { + reply: sinon.stub() + }; + await ping.execute(interaction); + assert.ok(interaction.reply.calledOnce); + assert.deepEqual(interaction.reply.getCalls()[0].args, ['Pong!']); + }); +}); \ No newline at end of file diff --git a/discord-bot/test/setup.js b/discord-bot/test/setup.js new file mode 100644 index 0000000..20f85a7 --- /dev/null +++ b/discord-bot/test/setup.js @@ -0,0 +1,21 @@ +'use strict'; + +require('dotenv').config({ path: `${__dirname}/../.env.test` }); + +const Bot = require('../models/bot'); +const { before } = require('mocha'); +const mongoose = require('../mongoose'); + +const uri = process.env.JSON_API_URL; +const jsonApiConnectOptions = { + username: process.env.JSON_API_AUTH_USERNAME, + password: process.env.JSON_API_AUTH_PASSWORD, + authUrl: process.env.JSON_API_AUTH_URL +}; + +before(async function() { + console.log('Connecting to', uri); + await mongoose.connect(uri, jsonApiConnectOptions); + await Bot.db.dropCollection('bots').catch(() => {}); + await Bot.createCollection(); +}); \ No newline at end of file diff --git a/netlify-functions-ecommerce/.env.test b/netlify-functions-ecommerce/.env.test new file mode 100644 index 0000000..f8c2e0c --- /dev/null +++ b/netlify-functions-ecommerce/.env.test @@ -0,0 +1,14 @@ +#Fill the Local JSON API related details only when NODE_ENV is set to 'jsonapi' +#Local JSON API URL for example: http://127.0.0.1:8181/v1/ecommerce_test where 'ecommerce_test' is the keyspace name +JSON_API_URL=http://127.0.0.1:8181/v1/ecommerce_test +#Auth URL for example: http://127.0.0.1:8081/v1/auth +JSON_API_AUTH_URL=http://127.0.0.1:8081/v1/auth +#Auth username and password +JSON_API_AUTH_USERNAME=cassandra +JSON_API_AUTH_PASSWORD=cassandra + +#Fill in Stripe related details if you want to see Stripe integration. +#Otherwise the sample app will bypass Stripe. +STRIPE_SECRET_KEY=test-key +STRIPE_SUCCESS_URL=http://127.0.0.1:8888/order-confirmation +STRIPE_CANCEL_URL=http://127.0.0.1:8888/cart \ No newline at end of file diff --git a/netlify-functions-ecommerce/.eslintignore b/netlify-functions-ecommerce/.eslintignore new file mode 100644 index 0000000..d70ebaa --- /dev/null +++ b/netlify-functions-ecommerce/.eslintignore @@ -0,0 +1 @@ +public \ No newline at end of file diff --git a/netlify-functions-ecommerce/.eslintrc.js b/netlify-functions-ecommerce/.eslintrc.js new file mode 100644 index 0000000..b5d1028 --- /dev/null +++ b/netlify-functions-ecommerce/.eslintrc.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + extends: [ + '@masteringjs' + ], + parserOptions: { + ecmaVersion: 2020 + }, + env: { + node: true, + es6: true + } +}; \ No newline at end of file diff --git a/netlify-functions-ecommerce/connect.js b/netlify-functions-ecommerce/connect.js index db7ca79..a8b8588 100644 --- a/netlify-functions-ecommerce/connect.js +++ b/netlify-functions-ecommerce/connect.js @@ -5,7 +5,7 @@ require('dotenv').config(); const mongoose = require('./mongoose'); require('./models'); -const {createAstraUri} = require("stargate-mongoose"); +const { createAstraUri } = require('stargate-mongoose'); let conn = null; diff --git a/netlify-functions-ecommerce/frontend/.eslintrc.js b/netlify-functions-ecommerce/frontend/.eslintrc.js new file mode 100644 index 0000000..c386687 --- /dev/null +++ b/netlify-functions-ecommerce/frontend/.eslintrc.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = { + env: { + browser: true + }, + globals: { + Vue: true, + VueRouter: true + } +}; \ No newline at end of file diff --git a/netlify-functions-ecommerce/frontend/src/BaseComponent.js b/netlify-functions-ecommerce/frontend/src/BaseComponent.js index 5c9270e..63a44a5 100644 --- a/netlify-functions-ecommerce/frontend/src/BaseComponent.js +++ b/netlify-functions-ecommerce/frontend/src/BaseComponent.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = (html, css) => { appendCSS(css); return { diff --git a/netlify-functions-ecommerce/frontend/src/cart/cart.js b/netlify-functions-ecommerce/frontend/src/cart/cart.js index 05fb51e..2366fdd 100644 --- a/netlify-functions-ecommerce/frontend/src/cart/cart.js +++ b/netlify-functions-ecommerce/frontend/src/cart/cart.js @@ -10,7 +10,7 @@ module.exports = app => app.component('cart', { computed: { cartTotal() { return '$' + this.state.cart.items.reduce((sum, item) => { - return sum + (+(item.quantity * this.product(item).price).toFixed(2)) + return sum + (+(item.quantity * this.product(item).price).toFixed(2)); }, 0).toFixed(2); } }, diff --git a/netlify-functions-ecommerce/netlify/functions/checkout.js b/netlify-functions-ecommerce/netlify/functions/checkout.js index a23e41f..4868105 100644 --- a/netlify-functions-ecommerce/netlify/functions/checkout.js +++ b/netlify-functions-ecommerce/netlify/functions/checkout.js @@ -39,7 +39,7 @@ const handler = async(event) => { return { statusCode: 200, body: JSON.stringify({ cart: cart, url: '/order-confirmation' }) - } + }; } const session = await stripe.checkout.sessions.create({ diff --git a/netlify-functions-ecommerce/package.json b/netlify-functions-ecommerce/package.json index 671a5cc..7e37822 100644 --- a/netlify-functions-ecommerce/package.json +++ b/netlify-functions-ecommerce/package.json @@ -12,7 +12,9 @@ "webpack": "5.x" }, "devDependencies": { + "@masteringjs/eslint-config": "0.0.1", "axios": "1.6.2", + "eslint": "8.54.0", "mocha": "10.2.0", "netlify-cli": "17.6.0" }, @@ -21,6 +23,7 @@ }, "scripts": { "build": "node ./frontend/build", + "lint": "eslint .", "seed": "node ./seed", "start": "netlify dev --dir public --functions netlify/functions", "test": "mocha ./test/*.test.js", diff --git a/netlify-functions-ecommerce/public/app.js b/netlify-functions-ecommerce/public/app.js index a95704d..921946f 100644 --- a/netlify-functions-ecommerce/public/app.js +++ b/netlify-functions-ecommerce/public/app.js @@ -1,4 +1,5 @@ /******/ (() => { // webpackBootstrap +/******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./frontend/src/BaseComponent.js": @@ -7,6 +8,8 @@ \***************************************/ /***/ ((module) => { + + module.exports = (html, css) => { appendCSS(css); return { @@ -42,7 +45,6 @@ function appendCSS(css) { \***********************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -55,7 +57,7 @@ module.exports = app => app.component('cart', { computed: { cartTotal() { return '$' + this.state.cart.items.reduce((sum, item) => { - return sum + (+(item.quantity * this.product(item).price).toFixed(2)) + return sum + (+(item.quantity * this.product(item).price).toFixed(2)); }, 0).toFixed(2); } }, @@ -100,7 +102,6 @@ module.exports = app => app.component('cart', { \***********************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -148,7 +149,6 @@ module.exports = app => app.component('home', { \***************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -166,7 +166,6 @@ module.exports = app => app.component('navbar', { \***************************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -207,7 +206,6 @@ module.exports = app => app.component('order-confirmation', { \*****************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -261,7 +259,6 @@ module.exports = app => app.component('product', { \*******************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -"use strict"; const BaseComponent = __webpack_require__(/*! ../BaseComponent */ "./frontend/src/BaseComponent.js"); @@ -309,7 +306,6 @@ module.exports = app => app.component('products', { \********************************/ /***/ ((module) => { -"use strict"; module.exports = [ @@ -343,7 +339,6 @@ module.exports = [ \************************************/ /***/ ((module) => { -"use strict"; module.exports = ".cart .cart-item {\n display: flex;\n gap: 10px;\n width: 100%;\n margin-bottom: 1em;\n}\n\n.cart .cart-item .product-image {\n width: 80px;\n border: 1px solid #ddd;\n border-radius: 8px;\n padding: 5px;\n}\n\n.cart .cart-item .product-image img {\n width: 100%;\n height: auto;\n vertical-align: middle;\n}\n\n.cart .cart-item .item-description {\n flex-grow: 1;\n}\n\n.cart .cart-item .item-description .name {\n margin-bottom: 0.5em;\n font-weight: bold;\n}\n\n.cart .cart-item .subtotal {\n width: 120px;\n font-weight: bold;\n text-align: right;\n}\n\n.cart .total {\n width: 100%;\n font-weight: bold;\n border-top: 1px solid #ddd;\n padding-top: 1em;\n display: flex;\n}\n\n.cart .total .total-text {\n width: 50%;\n}\n\n.cart .total .total-price {\n text-align: right;\n width: 50%;\n}\n\n.cart .checkout {\n text-align: center;\n}\n\n.cart .checkout button {\n margin-top: 1em;\n font-size: 2em;\n background-color: white;\n border: 2px solid #43783E;\n color: #43783E;\n padding: 0.5em 1em;\n border-radius: 16px;\n}\n\n.cart .checkout button:hover {\n color: white;\n background-color: #43783E;\n}"; /***/ }), @@ -354,7 +349,6 @@ module.exports = ".cart .cart-item {\n display: flex;\n gap: 10px;\n width: 1 \*************************************/ /***/ ((module) => { -"use strict"; module.exports = "