diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3170cd8..9388397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,15 @@ env: GOOGLE_CLIENT_ID: ${{secrets.GOOGLE_CLIENT_ID}} GOOGLE_CLIENT_SECRET: ${{secrets.GOOGLE_CLIENT_SECRET}} - STRIPE_SECRET_KEY: ${{secrets.STRIPE_SECRET_KEYT}} + TEST_USER_EMAIL: ${{secrets.TEST_USER_EMAIL}} + TEST_USER_PASS: ${{secrets.TEST_USER_PASS}} + TEST_VENDOR_EMAIL: ${{secrets.TEST_VENDOR_EMAIL}} + TEST_VENDOR1_EMAIL: ${{secrets.TEST_VENDOR1_EMAIL}} + TEST_BUYER_EMAIL: ${{secrets.TEST_BUYER_EMAIL}} + TEST_SAMPLE_BUYER_EMAIL: ${{secrets.TEST_SAMPLE_BUYER_EMAIL}} + TEST_VENDOR2_EMAIL: ${{secrets.TEST_VENDOR2_EMAIL}} + + STRIPE_SECRET_KEY: ${{secrets.STRIPE_SECRET_KEY}} jobs: build-lint-test-coverage: @@ -48,4 +56,4 @@ jobs: - name: Upload coverage report to Coveralls uses: coverallsapp/github-action@v2.2.3 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1500c37..b130fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ package-lock.json coverage/ dist /src/logs -.DS_Store \ No newline at end of file +.DS_Store diff --git "a/\\" "b/\\" new file mode 100644 index 0000000..29702f9 --- /dev/null +++ "b/\\" @@ -0,0 +1,55 @@ +Merge branch 'develop' of https://github.com/atlp-rwanda/knights-ecomm-be into ft-coverage + +# Conflicts: +# src/routes/index.ts +# +# It looks like you may be committing a merge. +# If this is not correct, please remove the file +# .git/MERGE_HEAD +# and try again. + + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# On branch ft-coverage +# Your branch is up to date with 'origin/ft-coverage'. +# +# All conflicts fixed but you are still merging. +# +# Changes to be committed: +# modified: package.json +# modified: src/__test__/cart.test.ts +# new file: src/__test__/isValid.test.ts +# new file: src/__test__/login.service.test.ts +# new file: src/__test__/notification.test.ts +# modified: src/__test__/test-assets/DatabaseCleanup.ts +# new file: src/__test__/user,Route.test.ts +# new file: src/__test__/user.profile.update.service.test.ts +# new file: src/controllers/notificationControllers.ts +# modified: src/controllers/orderController.ts +# new file: src/docs/notifications.yml +# modified: src/docs/orderDocs.yml +# new file: src/entities/Notification.ts +# new file: src/entities/NotificationItem.ts +# modified: src/entities/VendorOrderItem.ts +# modified: src/helper/verify.ts +# new file: src/routes/NoficationRoutes.ts +# modified: src/routes/ProductRoutes.ts +# modified: src/routes/index.ts +# modified: src/services/adminOrderServices/updateOrder.ts +# modified: src/services/couponServices/buyerApplyCoupon.ts +# modified: src/services/couponServices/createCouponService.ts +# modified: src/services/feedbackServices/createFeedback.ts +# modified: src/services/index.ts +# new file: src/services/notificationServices/deleteNotification.ts +# new file: src/services/notificationServices/getNotifications.ts +# new file: src/services/notificationServices/updateNotification.ts +# modified: src/services/orderServices/createOrder.ts +# modified: src/services/orderServices/getOrderService.ts +# modified: src/services/orderServices/updateOrderService.ts +# modified: src/services/userServices/userDisableTwoFactorAuth.ts +# modified: src/services/userServices/userEnableTwoFactorAuth.ts +# new file: src/utils/getNotifications.ts +# new file: src/utils/sendNotification.ts +# diff --git a/jest.config.ts b/jest.config.ts index 15175c6..9ef429f 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -10,7 +10,7 @@ export default { resetMocks: true, restoreMocks: true, collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', // Include all JavaScript/JSX files in the src directory + 'src/services/**/*.{ts,tsx}', // Include all JavaScript/JSX files in the src directory ], coveragePathIgnorePatterns: [ '/node_modules/', // Exclude the node_modules directory diff --git a/model.nlp b/model.nlp index 6aee508..f1df53b 100644 --- a/model.nlp +++ b/model.nlp @@ -2,1066 +2,1437 @@ "settings": { "languages": [ "en" - ], - "tag": "nlp", - "threshold": 0.5, - "autoLoad": true, - "autoSave": true, - "modelFileName": "model.nlp", - "executeActionsBeforeAnswers": false, - "calculateSentiment": true + ] }, "nluManager": { - "settings": { - "tag": "nlu-manager" - }, - "locales": [ + "settings": {}, + "languages": [ "en" ], - "languageNames": {}, + "intentDomains": {}, "domainManagers": { "en": { "settings": { - "locale": "en", - "trainByDomain": false, - "tag": "domain-manager-en", - "nluByDomain": { - "default": { - "className": "NeuralNlu", - "settings": {} - } - }, - "useStemDict": true + "language": "en", + "useNoneFeature": true, + "config": { + "activation": "leaky-relu", + "hiddenLayers": [], + "iterations": 20000, + "learningRate": 0.7, + "errorThresh": 0.00005, + "momentum": 0.5, + "deltaErrorThresh": 0.000001, + "maxDecimals": 9, + "leakyReluAlpha": 0.08, + "timeout": 120000 + } }, + "language": "en", + "nluClassName": "BrainNLU", + "useMasterDomain": true, + "trainByDomain": false, + "keepStopwords": true, "stemDict": { "can,cancel,i,my,order": { - "intent": "cancel", - "domain": "default" - }, - "cancel,is,my,order,whi": { - "intent": "cancel", - "domain": "default" - }, - "can,cancel,do,how,i,my,order": { - "intent": "cancel", - "domain": "default" - }, - "an,can,cancel,i,order": { - "intent": "cancel", - "domain": "default" - }, - "ani,but,confirm,did,do,get,i,my,not,order,payment,process,should,success,was,what": { - "intent": "confirm", - "domain": "default" - }, - "but,complet,confirm,no,payment,was": { - "intent": "confirm", - "domain": "default" - }, - "confirm,not,order,was": { - "intent": "confirm", - "domain": "default" - }, - "but,confirm,deduct,is,money,not,order": { - "intent": "confirm", - "domain": "default" - }, - "delay,is,my,order,whi": { - "intent": "delay", - "domain": "default" - }, - "delay,did,get,my,order": { - "intent": "delay", - "domain": "default" - }, - "deliv,is,my,not,order,whi,yet": { - "intent": "delivery", - "domain": "default" - }, - "deliveri,do,get,i,my,when": { - "intent": "delivery", - "domain": "default" - }, - "deliveri,doe,how,long,take": { - "intent": "delivery", - "domain": "default" - }, - "doe,how,long,ship,take": { - "intent": "delivery", - "domain": "default" - }, - "about,deliveri,me,my,pleas,tell": { - "intent": "delivery", - "domain": "default" - }, - "goodby": { - "intent": "greetings.bye", - "domain": "default" - }, - "bye,care,take": { - "intent": "greetings.bye", - "domain": "default" - }, - "later,see,you": { - "intent": "greetings.bye", - "domain": "default" - }, - "bye,for,now": { - "intent": "greetings.bye", - "domain": "default" - }, - "go,i,must": { - "intent": "greetings.bye", - "domain": "default" - }, - "hello": { - "intent": "greetings.hello", - "domain": "default" - }, - "hi": { - "intent": "greetings.hello", - "domain": "default" - }, - "howdi": { - "intent": "greetings.hello", - "domain": "default" - }, - "greet": { - "intent": "greetings.hello", - "domain": "default" - }, - "anyon,is,there": { - "intent": "greetings.hello", - "domain": "default" - }, - "day,good": { - "intent": "greetings.hello", - "domain": "default" - }, - "do,have,item,which,you": { - "intent": "items", - "domain": "default" - }, - "are,item,kind,of,there,what": { - "intent": "items", - "domain": "default" - }, - "do,sell,what,you": { - "intent": "items", - "domain": "default" - }, - "do,offer,what,you": { - "intent": "items", - "domain": "default" - }, - "buy,can,i,what": { - "intent": "items", - "domain": "default" - }, - "am,for,i,look": { - "intent": "items", - "domain": "default" - }, - "ani,do,have,you": { - "intent": "items", - "domain": "default" - }, - "am,i,in,interest": { - "intent": "items", - "domain": "default" - }, - "can,have,i,in,see,what,you": { - "intent": "items", - "domain": "default" - }, - "a,buy,i,to,want": { - "intent": "items", - "domain": "default" - }, - "am,for,i,like,look,someth,this": { - "intent": "items", - "domain": "default" - }, - "are,item,most,popular,what,your": { - "intent": "items", - "domain": "default" - }, - "are,best,deal,of,some,what,your": { - "intent": "items", - "domain": "default" - }, - "ani,arriv,do,have,new,you": { - "intent": "items", - "domain": "default" - }, - "help,more,need": { - "intent": "more.contact", - "domain": "default" - }, - "help,me,more": { - "intent": "more.contact", - "domain": "default" - }, - "agent,an,can,i,talk,to": { - "intent": "more.help", - "domain": "default" - }, - "call,can,custom,i,servic": { - "intent": "more.help", - "domain": "default" - }, - "custom,number,support": { - "intent": "more.help", - "domain": "default" - }, - "contact,custom,how,servic,to": { - "intent": "more.help", - "domain": "default" - }, - "custom,number,servic": { - "intent": "more.help", - "domain": "default" - }, - "contact,for,help,number": { - "intent": "more.help", - "domain": "default" - }, - "helplin,number": { - "intent": "more.help", - "domain": "default" - }, - "a,becom,how,seller,to": { - "intent": "more.help", - "domain": "default" - }, - "a,contact,how,seller,to": { - "intent": "more.help", - "domain": "default" - }, - "is,my,order,status,what": { - "intent": "order", - "domain": "default" - }, - "i,know,my,return,status,to,want": { - "intent": "order", - "domain": "default" - }, - "how,return,status,to": { - "intent": "order", - "domain": "default" - }, - "card,credit,do,take,you": { - "intent": "payments", - "domain": "default" - }, - "accept,do,mastercard,you": { - "intent": "payments", - "domain": "default" - }, - "can,cash,i,pay,with": { - "intent": "payments", - "domain": "default" - }, - "are,cash,onli,you": { - "intent": "payments", - "domain": "default" - }, - "are,method,payment,what,your": { - "intent": "payments", - "domain": "default" - }, - "do,how,i,pay": { - "intent": "payments", - "domain": "default" - }, - "are,how,you": { - "intent": "personal", - "domain": "default" - }, - "are,do,how,you": { - "intent": "personal", - "domain": "default" - }, - "day,how,is,your": { - "intent": "personal", - "domain": "default" - }, - "can,chang,how,i,inform,my,profil": { - "intent": "profile.info", - "domain": "default" - }, - "chang,i,my,password,to,want": { - "intent": "profile.info", - "domain": "default" - }, - "chang,i,my,number,phone,to,want": { - "intent": "profile.info", - "domain": "default" - }, - "address,chang,i,my,to,want": { - "intent": "profile.info", - "domain": "default" - }, - "i,my,password,reset,to,want": { - "intent": "profile.info", - "domain": "default" - }, - "account,delet,i,my,to,want": { - "intent": "profile.info", - "domain": "default" - }, - "account,delet,my": { - "intent": "profile.info", - "domain": "default" - }, - "common,delay,deliveri,for,reason": { - "intent": "reason", - "domain": "default" - }, - "delay,for,reason": { - "intent": "reason", - "domain": "default" - }, - "delay,deliveri": { - "intent": "reason", - "domain": "default" - }, - "an,can,i,item,refund": { - "intent": "refund.demanding", - "domain": "default" - }, - "an,i,item,refund,to,want": { - "intent": "refund.demanding", - "domain": "default" - }, - "can,i,my,order,refund": { - "intent": "refund.demanding", - "domain": "default" - }, - "are,avail,refund": { - "intent": "refund.demanding", - "domain": "default" - }, - "credit,is,it,not,refund,status,the,when,whi": { - "intent": "refund.status", - "domain": "default" - }, - "even,is,no,refund,status,though": { - "intent": "refund.status", - "domain": "default" - }, - "no,refund,say,status,when": { - "intent": "refund.status", - "domain": "default" - }, - "did,i,money,my,not,receiv,refund": { - "intent": "refund.status", - "domain": "default" - }, - "money,not,receiv,refund": { - "intent": "refund.status", - "domain": "default" - }, - "thank": { - "intent": "thanks", - "domain": "default" - }, - "thank,you": { - "intent": "thanks", - "domain": "default" - }, - "help,is,that": { - "intent": "thanks", - "domain": "default" - }, - "a,is,lot,thank": { - "intent": "thanks", - "domain": "default" - }, - "thx": { - "intent": "thanks", - "domain": "default" - }, - "thnks": { - "intent": "thanks", - "domain": "default" - }, - "can,how,i,my,order,track": { - "intent": "track", - "domain": "default" - }, - "i,my,order,to,track,want": { - "intent": "track", - "domain": "default" - }, - "can,i,my,order,track": { - "intent": "track", - "domain": "default" - }, - "order,track": { - "intent": "track", - "domain": "default" - }, - "am,good,i": { - "intent": "user.response", - "domain": "default" - }, - "good,im": { - "intent": "user.response", - "domain": "default" - }, - "do,good,im": { - "intent": "user.response", - "domain": "default" - }, - "am,i,okay": { - "intent": "user.response", - "domain": "default" - }, - "a,how,to,use,voucher": { - "intent": "voucher", - "domain": "default" - }, - "a,can,i,use,voucher": { - "intent": "voucher", - "domain": "default" - } - }, - "intentDict": { - "cancel": "default", - "confirm": "default", - "delay": "default", - "delivery": "default", - "greetings.bye": "default", - "greetings.hello": "default", - "items": "default", - "more.contact": "default", - "more.help": "default", - "order": "default", - "payments": "default", - "personal": "default", - "profile.info": "default", - "reason": "default", - "refund.demanding": "default", - "refund.status": "default", - "thanks": "default", - "track": "default", - "user.response": "default", - "voucher": "default" - }, - "sentences": [ - { "domain": "default", - "utterance": "Can I cancel my order?", "intent": "cancel" }, - { + "cancel,is,my,order,whi": { "domain": "default", - "utterance": "Why is my order cancelled?", "intent": "cancel" }, - { + "can,cancel,do,how,i,my,order": { "domain": "default", - "utterance": "How do I can cancel my order", "intent": "cancel" }, - { + "an,can,cancel,i,order": { "domain": "default", - "utterance": "Can I cancel an order", "intent": "cancel" }, - { + "ani,but,confirm,did,do,get,i,i,my,not,order,payment,process,should,success,was,what": { "domain": "default", - "utterance": "My payment was processed successfully but I didn't get any order confirmation. What should I do?", "intent": "confirm" }, - { + "but,complet,confirm,no,payment,was": { "domain": "default", - "utterance": "Payment was completed but no confirmation", "intent": "confirm" }, - { + "confirm,not,order,was": { "domain": "default", - "utterance": "Order was not confirmed", "intent": "confirm" }, - { + "but,confirm,deduct,is,money,not,order": { "domain": "default", - "utterance": "Money deducted but order is not confirmed", "intent": "confirm" }, - { - "domain": "default", - "utterance": "Why is my order delayed? ", - "intent": "delay" - }, - { - "domain": "default", - "utterance": "Did my order get delayed?", - "intent": "delay" - }, - { + "delay,is,my,order,whi": { "domain": "default", - "utterance": "Why is my order not delivered yet?", "intent": "delay" }, - { + "delay,did,get,my,order": { "domain": "default", - "utterance": "When do I get my delivery?", "intent": "delay" }, - { + "deliveri,doe,how,long,take": { "domain": "default", - "utterance": "How long does delivery take?", "intent": "delivery" }, - { + "doe,how,long,ship,take": { "domain": "default", - "utterance": "How long does shipping take?", "intent": "delivery" }, - { + "about,deliveri,me,my,pleas,tell": { "domain": "default", - "utterance": "Please Tell me about my delivery", "intent": "delivery" }, - { + "deliveri,do,get,i,my,when": { "domain": "default", - "utterance": "When do I get my delivery?", "intent": "delivery" }, - { + "deliv,is,my,not,order,whi,yet": { "domain": "default", - "utterance": "Why is my order not delivered yet", "intent": "delivery" }, - { + "goodby": { "domain": "default", - "utterance": "goodbye", "intent": "greetings.bye" }, - { + "bye,care,take": { "domain": "default", - "utterance": "bye take care", "intent": "greetings.bye" }, - { + "later,see,you": { "domain": "default", - "utterance": "see you later", "intent": "greetings.bye" }, - { + "bye,for,now": { "domain": "default", - "utterance": "bye for now", "intent": "greetings.bye" }, - { + "go,i,must": { "domain": "default", - "utterance": "i must go", "intent": "greetings.bye" }, - { - "domain": "default", - "utterance": "hello", - "intent": "greetings.hello" - }, - { + "hi": { "domain": "default", - "utterance": "hi", "intent": "greetings.hello" }, - { + "howdi": { "domain": "default", - "utterance": "howdy", "intent": "greetings.hello" }, - { + "greet": { "domain": "default", - "utterance": "Greetings", "intent": "greetings.hello" }, - { + "anyon,is,there": { "domain": "default", - "utterance": "Is anyone there?", "intent": "greetings.hello" }, - { + "hello": { "domain": "default", - "utterance": "Hello", "intent": "greetings.hello" }, - { + "day,good": { "domain": "default", - "utterance": "Good day", "intent": "greetings.hello" }, - { + "do,have,item,which,you": { "domain": "default", - "utterance": "Which items do you have?", "intent": "items" }, - { + "are,item,kind,of,there,what": { "domain": "default", - "utterance": "What kinds of items are there?", "intent": "items" }, - { + "do,sell,what,you": { "domain": "default", - "utterance": "What do you sell?", "intent": "items" }, - { + "do,offer,what,you": { "domain": "default", - "utterance": "What do you offer?", "intent": "items" }, - { + "buy,can,i,what": { "domain": "default", - "utterance": "What can I buy?", "intent": "items" }, - { + "am,for,i,look": { "domain": "default", - "utterance": "I'm looking for...", "intent": "items" }, - { + "ani,do,have,you": { "domain": "default", - "utterance": "Do you have any...", "intent": "items" }, - { + "am,i,in,interest": { "domain": "default", - "utterance": "I'm interested in...", "intent": "items" }, - { + "can,have,i,in,see,what,you": { "domain": "default", - "utterance": "Can I see what you have in...", "intent": "items" }, - { + "a,buy,i,to,want": { "domain": "default", - "utterance": "I want to buy a...", "intent": "items" }, - { + "am,for,i,like,look,someth,this": { "domain": "default", - "utterance": "I'm looking for something like this...", "intent": "items" }, - { + "are,item,most,popular,what,your": { "domain": "default", - "utterance": "What are your most popular items?", "intent": "items" }, - { + "are,best,deal,of,some,what,your": { "domain": "default", - "utterance": "What are some of your best deals?", "intent": "items" }, - { + "ani,arriv,do,have,new,you": { "domain": "default", - "utterance": "Do you have any new arrivals?", "intent": "items" }, - { + "help,more,need": { "domain": "default", - "utterance": "Need more help", "intent": "more.contact" }, - { + "help,me,more": { "domain": "default", - "utterance": "Help me more", "intent": "more.contact" }, - { + "agent,an,can,i,talk,to": { "domain": "default", - "utterance": "can I talk to an agent", "intent": "more.help" }, - { + "call,can,custom,i,servic": { "domain": "default", - "utterance": "can I call customer service", "intent": "more.help" }, - { + "custom,number,support": { "domain": "default", - "utterance": "customer support number", "intent": "more.help" }, - { + "contact,custom,how,servic,to": { "domain": "default", - "utterance": "how to contact customer service", "intent": "more.help" }, - { + "custom,number,servic": { "domain": "default", - "utterance": "customer service number", "intent": "more.help" }, - { + "contact,for,help,number": { "domain": "default", - "utterance": "contact number for help", "intent": "more.help" }, - { + "helplin,number": { "domain": "default", - "utterance": "helpline number", "intent": "more.help" }, - { + "a,becom,how,seller,to": { "domain": "default", - "utterance": "How to become a seller", "intent": "more.help" }, - { + "a,contact,how,seller,to": { "domain": "default", - "utterance": "How to contact a seller", "intent": "more.help" }, - { + "is,my,order,status,what": { "domain": "default", - "utterance": "What is my order status", "intent": "order" }, - { + "i,know,my,return,status,to,want": { "domain": "default", - "utterance": "I want to know my return status", "intent": "order" }, - { + "how,return,status,to": { "domain": "default", - "utterance": "How to return status", "intent": "order" }, - { + "card,credit,do,take,you": { "domain": "default", - "utterance": "Do you take credit cards?", "intent": "payments" }, - { + "accept,do,mastercard,you": { "domain": "default", - "utterance": "Do you accept Mastercard?", "intent": "payments" }, - { + "can,cash,i,pay,with": { "domain": "default", - "utterance": "Can I pay with Cash?", "intent": "payments" }, - { + "are,cash,onli,you": { "domain": "default", - "utterance": "Are you cash only?", "intent": "payments" }, - { + "are,method,payment,what,your": { "domain": "default", - "utterance": "What are your payment methods?", "intent": "payments" }, - { + "do,how,i,pay": { "domain": "default", - "utterance": "How do I pay?", "intent": "payments" }, - { + "are,how,you": { "domain": "default", - "utterance": "How are you?", "intent": "personal" }, - { + "are,do,how,you": { "domain": "default", - "utterance": "How are you doing?", "intent": "personal" }, - { + "day,how,is,your": { "domain": "default", - "utterance": "How is your day?", "intent": "personal" }, - { + "can,chang,how,i,inform,my,profil": { "domain": "default", - "utterance": "How can I change my profile information", "intent": "profile.info" }, - { + "chang,i,my,password,to,want": { "domain": "default", - "utterance": "I want to change my password", "intent": "profile.info" }, - { + "chang,i,my,number,phone,to,want": { "domain": "default", - "utterance": "I want to change my phone number", "intent": "profile.info" }, - { + "address,chang,i,my,to,want": { "domain": "default", - "utterance": "I want to change my address", "intent": "profile.info" }, - { + "i,my,password,reset,to,want": { "domain": "default", - "utterance": "I want to Reset my password", "intent": "profile.info" }, - { + "account,delet,i,my,to,want": { "domain": "default", - "utterance": "I want to delete my account", "intent": "profile.info" }, - { + "account,delet,my": { "domain": "default", - "utterance": "delete my account", "intent": "profile.info" }, - { - "domain": "default", - "utterance": "Common reasons for delivery delay", - "intent": "reason" - }, - { + "common,delay,deliveri,for,reason": { "domain": "default", - "utterance": "common reasons for delivery delay", "intent": "reason" }, - { + "delay,for,reason": { "domain": "default", - "utterance": "reasons for delay", "intent": "reason" }, - { + "delay,deliveri": { "domain": "default", - "utterance": "delivery delay", "intent": "reason" }, - { + "an,can,i,item,refund": { "domain": "default", - "utterance": "Can I refund an item.", "intent": "refund.demanding" }, - { + "an,i,item,refund,to,want": { "domain": "default", - "utterance": "I want to refund an item", "intent": "refund.demanding" }, - { + "can,i,my,order,refund": { "domain": "default", - "utterance": "can I refund my order", "intent": "refund.demanding" }, - { + "are,avail,refund": { "domain": "default", - "utterance": "Are refunds available", "intent": "refund.demanding" }, - { + "credit,is,is,it,not,refund,status,the,when,whi": { "domain": "default", - "utterance": "Why is the status Refunded when it's not credited?", "intent": "refund.status" }, - { + "even,is,no,refund,refund,status,though": { "domain": "default", - "utterance": "No refund even though status is refunded", "intent": "refund.status" }, - { + "no,refund,refund,say,status,when": { "domain": "default", - "utterance": "No refund when status says refunded", "intent": "refund.status" }, - { + "did,i,money,my,not,receiv,refund": { "domain": "default", - "utterance": "I did not receive my refund money", "intent": "refund.status" }, - { + "money,not,receiv,refund": { "domain": "default", - "utterance": "Refund money not received", "intent": "refund.status" }, - { + "thank": { "domain": "default", - "utterance": "Thanks", "intent": "thanks" }, - { + "thank,you": { "domain": "default", - "utterance": "Thank you", "intent": "thanks" }, - { + "help,is,that": { "domain": "default", - "utterance": "That's helpful", "intent": "thanks" }, - { + "a,is,lot,thank": { "domain": "default", - "utterance": "Thank's a lot!", "intent": "thanks" }, - { + "thx": { "domain": "default", - "utterance": "thx", "intent": "thanks" }, - { + "thnks": { "domain": "default", - "utterance": "thnks", "intent": "thanks" }, - { + "can,how,i,my,order,track": { "domain": "default", - "utterance": "How can I track my order", "intent": "track" }, - { + "i,my,order,to,track,want": { "domain": "default", - "utterance": "I want to track my order", "intent": "track" }, - { + "can,i,my,order,track": { "domain": "default", - "utterance": "Can I track my order", "intent": "track" }, - { + "order,track": { "domain": "default", - "utterance": "Track order", "intent": "track" }, - { - "domain": "default", - "utterance": "I'm good", - "intent": "user.response" - }, - { + "good,im": { "domain": "default", - "utterance": "Im good", "intent": "user.response" }, - { + "do,good,im": { "domain": "default", - "utterance": "Im doing good", "intent": "user.response" }, - { + "am,good,i": { "domain": "default", - "utterance": "I am good", "intent": "user.response" }, - { + "am,i,okay": { "domain": "default", - "utterance": "I am okay", "intent": "user.response" }, - { - "domain": "default", - "utterance": "How to use a voucher?", - "intent": "voucher" - }, - { + "a,can,i,use,voucher": { "domain": "default", - "utterance": "Can I use a voucher?", "intent": "voucher" }, - { + "a,how,to,use,voucher": { "domain": "default", - "utterance": "How to use a voucher?", "intent": "voucher" } - ], + }, + "intentDict": { + "cancel": "default", + "confirm": "default", + "delay": "default", + "delivery": "default", + "greetings.bye": "default", + "greetings.hello": "default", + "items": "default", + "more.contact": "default", + "more.help": "default", + "order": "default", + "payments": "default", + "personal": "default", + "profile.info": "default", + "reason": "default", + "refund.demanding": "default", + "refund.status": "default", + "thanks": "default", + "track": "default", + "user.response": "default", + "voucher": "default" + }, + "useStemDict": true, "domains": { "master_domain": { "settings": { - "locale": "en", - "tag": "nlu-en", - "keepStopwords": true, - "nonefeatureValue": 1, - "nonedeltaMultiplier": 1.2, - "spellCheck": false, - "spellCheckDistance": 1, - "filterZeros": true, - "log": true + "language": "en", + "useNoneFeature": true, + "config": { + "activation": "leaky-relu", + "hiddenLayers": [], + "iterations": 20000, + "learningRate": 0.7, + "errorThresh": 0.00005, + "momentum": 0.5, + "deltaErrorThresh": 0.000001, + "maxDecimals": 9, + "leakyReluAlpha": 0.08, + "timeout": 120000 + } }, + "language": "en", + "keepStopwords": true, + "docs": [ + { + "intent": "cancel", + "tokens": [ + "can", + "i", + "cancel", + "my", + "order" + ] + }, + { + "intent": "cancel", + "tokens": [ + "whi", + "is", + "my", + "order", + "cancel" + ] + }, + { + "intent": "cancel", + "tokens": [ + "how", + "do", + "i", + "can", + "cancel", + "my", + "order" + ] + }, + { + "intent": "cancel", + "tokens": [ + "can", + "i", + "cancel", + "an", + "order" + ] + }, + { + "intent": "confirm", + "tokens": [ + "my", + "payment", + "was", + "process", + "success", + "but", + "i", + "did", + "not", + "get", + "ani", + "order", + "confirm", + "what", + "should", + "i", + "do" + ] + }, + { + "intent": "confirm", + "tokens": [ + "payment", + "was", + "complet", + "but", + "no", + "confirm" + ] + }, + { + "intent": "confirm", + "tokens": [ + "order", + "was", + "not", + "confirm" + ] + }, + { + "intent": "confirm", + "tokens": [ + "money", + "deduct", + "but", + "order", + "is", + "not", + "confirm" + ] + }, + { + "intent": "delay", + "tokens": [ + "whi", + "is", + "my", + "order", + "delay" + ] + }, + { + "intent": "delay", + "tokens": [ + "did", + "my", + "order", + "get", + "delay" + ] + }, + { + "intent": "delivery", + "tokens": [ + "how", + "long", + "doe", + "deliveri", + "take" + ] + }, + { + "intent": "delivery", + "tokens": [ + "how", + "long", + "doe", + "ship", + "take" + ] + }, + { + "intent": "delivery", + "tokens": [ + "pleas", + "tell", + "me", + "about", + "my", + "deliveri" + ] + }, + { + "intent": "greetings.bye", + "tokens": [ + "goodby" + ] + }, + { + "intent": "greetings.bye", + "tokens": [ + "bye", + "take", + "care" + ] + }, + { + "intent": "greetings.bye", + "tokens": [ + "see", + "you", + "later" + ] + }, + { + "intent": "greetings.bye", + "tokens": [ + "bye", + "for", + "now" + ] + }, + { + "intent": "greetings.bye", + "tokens": [ + "i", + "must", + "go" + ] + }, + { + "intent": "greetings.hello", + "tokens": [ + "hi" + ] + }, + { + "intent": "greetings.hello", + "tokens": [ + "howdi" + ] + }, + { + "intent": "greetings.hello", + "tokens": [ + "greet" + ] + }, + { + "intent": "greetings.hello", + "tokens": [ + "is", + "anyon", + "there" + ] + }, + { + "intent": "greetings.hello", + "tokens": [ + "good", + "day" + ] + }, + { + "intent": "items", + "tokens": [ + "which", + "item", + "do", + "you", + "have" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "kind", + "of", + "item", + "are", + "there" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "do", + "you", + "sell" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "do", + "you", + "offer" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "can", + "i", + "buy" + ] + }, + { + "intent": "items", + "tokens": [ + "i", + "am", + "look", + "for" + ] + }, + { + "intent": "items", + "tokens": [ + "do", + "you", + "have", + "ani" + ] + }, + { + "intent": "items", + "tokens": [ + "i", + "am", + "interest", + "in" + ] + }, + { + "intent": "items", + "tokens": [ + "can", + "i", + "see", + "what", + "you", + "have", + "in" + ] + }, + { + "intent": "items", + "tokens": [ + "i", + "want", + "to", + "buy", + "a" + ] + }, + { + "intent": "items", + "tokens": [ + "i", + "am", + "look", + "for", + "someth", + "like", + "this" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "are", + "your", + "most", + "popular", + "item" + ] + }, + { + "intent": "items", + "tokens": [ + "what", + "are", + "some", + "of", + "your", + "best", + "deal" + ] + }, + { + "intent": "items", + "tokens": [ + "do", + "you", + "have", + "ani", + "new", + "arriv" + ] + }, + { + "intent": "more.contact", + "tokens": [ + "need", + "more", + "help" + ] + }, + { + "intent": "more.contact", + "tokens": [ + "help", + "me", + "more" + ] + }, + { + "intent": "more.help", + "tokens": [ + "can", + "i", + "talk", + "to", + "an", + "agent" + ] + }, + { + "intent": "more.help", + "tokens": [ + "can", + "i", + "call", + "custom", + "servic" + ] + }, + { + "intent": "more.help", + "tokens": [ + "custom", + "support", + "number" + ] + }, + { + "intent": "more.help", + "tokens": [ + "how", + "to", + "contact", + "custom", + "servic" + ] + }, + { + "intent": "more.help", + "tokens": [ + "custom", + "servic", + "number" + ] + }, + { + "intent": "more.help", + "tokens": [ + "contact", + "number", + "for", + "help" + ] + }, + { + "intent": "more.help", + "tokens": [ + "helplin", + "number" + ] + }, + { + "intent": "more.help", + "tokens": [ + "how", + "to", + "becom", + "a", + "seller" + ] + }, + { + "intent": "more.help", + "tokens": [ + "how", + "to", + "contact", + "a", + "seller" + ] + }, + { + "intent": "order", + "tokens": [ + "what", + "is", + "my", + "order", + "status" + ] + }, + { + "intent": "order", + "tokens": [ + "i", + "want", + "to", + "know", + "my", + "return", + "status" + ] + }, + { + "intent": "order", + "tokens": [ + "how", + "to", + "return", + "status" + ] + }, + { + "intent": "payments", + "tokens": [ + "do", + "you", + "take", + "credit", + "card" + ] + }, + { + "intent": "payments", + "tokens": [ + "do", + "you", + "accept", + "mastercard" + ] + }, + { + "intent": "payments", + "tokens": [ + "can", + "i", + "pay", + "with", + "cash" + ] + }, + { + "intent": "payments", + "tokens": [ + "are", + "you", + "cash", + "onli" + ] + }, + { + "intent": "payments", + "tokens": [ + "what", + "are", + "your", + "payment", + "method" + ] + }, + { + "intent": "payments", + "tokens": [ + "how", + "do", + "i", + "pay" + ] + }, + { + "intent": "personal", + "tokens": [ + "how", + "are", + "you" + ] + }, + { + "intent": "personal", + "tokens": [ + "how", + "are", + "you", + "do" + ] + }, + { + "intent": "personal", + "tokens": [ + "how", + "is", + "your", + "day" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "how", + "can", + "i", + "chang", + "my", + "profil", + "inform" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "i", + "want", + "to", + "chang", + "my", + "password" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "i", + "want", + "to", + "chang", + "my", + "phone", + "number" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "i", + "want", + "to", + "chang", + "my", + "address" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "i", + "want", + "to", + "reset", + "my", + "password" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "i", + "want", + "to", + "delet", + "my", + "account" + ] + }, + { + "intent": "profile.info", + "tokens": [ + "delet", + "my", + "account" + ] + }, + { + "intent": "reason", + "tokens": [ + "common", + "reason", + "for", + "deliveri", + "delay" + ] + }, + { + "intent": "reason", + "tokens": [ + "reason", + "for", + "delay" + ] + }, + { + "intent": "reason", + "tokens": [ + "deliveri", + "delay" + ] + }, + { + "intent": "refund.demanding", + "tokens": [ + "can", + "i", + "refund", + "an", + "item" + ] + }, + { + "intent": "refund.demanding", + "tokens": [ + "i", + "want", + "to", + "refund", + "an", + "item" + ] + }, + { + "intent": "refund.demanding", + "tokens": [ + "can", + "i", + "refund", + "my", + "order" + ] + }, + { + "intent": "refund.demanding", + "tokens": [ + "are", + "refund", + "avail" + ] + }, + { + "intent": "refund.status", + "tokens": [ + "whi", + "is", + "the", + "status", + "refund", + "when", + "it", + "is", + "not", + "credit" + ] + }, + { + "intent": "refund.status", + "tokens": [ + "no", + "refund", + "even", + "though", + "status", + "is", + "refund" + ] + }, + { + "intent": "refund.status", + "tokens": [ + "no", + "refund", + "when", + "status", + "say", + "refund" + ] + }, + { + "intent": "refund.status", + "tokens": [ + "i", + "did", + "not", + "receiv", + "my", + "refund", + "money" + ] + }, + { + "intent": "refund.status", + "tokens": [ + "refund", + "money", + "not", + "receiv" + ] + }, + { + "intent": "thanks", + "tokens": [ + "thank" + ] + }, + { + "intent": "thanks", + "tokens": [ + "thank", + "you" + ] + }, + { + "intent": "thanks", + "tokens": [ + "that", + "is", + "help" + ] + }, + { + "intent": "thanks", + "tokens": [ + "thank", + "is", + "a", + "lot" + ] + }, + { + "intent": "thanks", + "tokens": [ + "thx" + ] + }, + { + "intent": "thanks", + "tokens": [ + "thnks" + ] + }, + { + "intent": "track", + "tokens": [ + "how", + "can", + "i", + "track", + "my", + "order" + ] + }, + { + "intent": "track", + "tokens": [ + "i", + "want", + "to", + "track", + "my", + "order" + ] + }, + { + "intent": "track", + "tokens": [ + "can", + "i", + "track", + "my", + "order" + ] + }, + { + "intent": "track", + "tokens": [ + "track", + "order" + ] + }, + { + "intent": "user.response", + "tokens": [ + "im", + "good" + ] + }, + { + "intent": "user.response", + "tokens": [ + "im", + "do", + "good" + ] + }, + { + "intent": "user.response", + "tokens": [ + "i", + "am", + "okay" + ] + }, + { + "intent": "voucher", + "tokens": [ + "can", + "i", + "use", + "a", + "voucher" + ] + } + ], "features": { - "can": 1, - "i": 1, - "cancel": 1, - "my": 1, - "order": 1, - "whi": 1, - "is": 1, - "how": 1, - "do": 1, - "an": 1, - "payment": 1, - "was": 1, + "can": 14, + "i": 32, + "cancel": 4, + "my": 21, + "order": 15, + "whi": 3, + "is": 11, + "how": 13, + "do": 12, + "an": 4, + "payment": 3, + "was": 3, "process": 1, "success": 1, - "but": 1, - "did": 1, - "not": 1, - "get": 1, - "ani": 1, - "confirm": 1, - "what": 1, + "but": 3, + "did": 3, + "not": 6, + "get": 2, + "ani": 3, + "confirm": 4, + "what": 10, "should": 1, "complet": 1, - "no": 1, - "money": 1, + "no": 3, + "money": 3, "deduct": 1, - "delay": 1, - "deliv": 1, - "yet": 1, - "when": 1, - "deliveri": 1, - "long": 1, - "doe": 1, - "take": 1, + "delay": 5, + "deliveri": 4, + "long": 2, + "doe": 2, + "take": 4, "ship": 1, "pleas": 1, "tell": 1, - "me": 1, + "me": 2, "about": 1, "goodby": 1, - "bye": 1, + "bye": 2, "care": 1, - "see": 1, - "you": 1, + "see": 2, + "you": 13, "later": 1, - "for": 1, + "for": 6, "now": 1, "must": 1, "go": 1, - "hello": 1, "hi": 1, "howdi": 1, "greet": 1, "anyon": 1, - "there": 1, - "good": 1, - "day": 1, + "there": 2, + "good": 3, + "day": 2, "which": 1, - "item": 1, - "have": 1, + "item": 5, + "have": 4, "kind": 1, - "of": 1, - "are": 1, + "of": 2, + "are": 8, "sell": 1, "offer": 1, - "buy": 1, - "am": 1, - "look": 1, + "buy": 2, + "am": 4, + "look": 2, "interest": 1, - "in": 1, - "want": 1, - "to": 1, - "a": 1, + "in": 2, + "want": 9, + "to": 14, + "a": 5, "someth": 1, "like": 1, "this": 1, - "your": 1, + "your": 4, "most": 1, "popular": 1, "some": 1, @@ -1070,3954 +1441,2344 @@ "new": 1, "arriv": 1, "need": 1, - "more": 1, - "help": 1, + "more": 2, + "help": 4, "talk": 1, "agent": 1, "call": 1, - "custom": 1, - "servic": 1, + "custom": 4, + "servic": 3, "support": 1, - "number": 1, - "contact": 1, + "number": 5, + "contact": 3, "helplin": 1, "becom": 1, - "seller": 1, - "status": 1, + "seller": 2, + "status": 6, "know": 1, - "return": 1, - "credit": 1, + "return": 2, + "credit": 2, "card": 1, "accept": 1, "mastercard": 1, - "pay": 1, + "pay": 2, "with": 1, - "cash": 1, + "cash": 2, "onli": 1, "method": 1, - "chang": 1, + "chang": 4, "profil": 1, "inform": 1, - "password": 1, + "password": 2, "phone": 1, "address": 1, "reset": 1, - "delet": 1, - "account": 1, + "delet": 2, + "account": 2, "common": 1, - "reason": 1, - "refund": 1, + "reason": 2, + "refund": 11, "avail": 1, "the": 1, + "when": 2, "it": 1, "even": 1, "though": 1, "say": 1, - "receiv": 1, - "thank": 1, + "receiv": 2, + "thank": 3, "that": 1, "lot": 1, "thx": 1, "thnks": 1, - "track": 1, - "im": 1, + "track": 4, + "im": 2, "okay": 1, "use": 1, "voucher": 1 }, - "intents": { - "cancel": 1, - "confirm": 1, - "delay": 1, - "delivery": 1, - "greetings.bye": 1, - "greetings.hello": 1, - "items": 1, - "more.contact": 1, - "more.help": 1, - "order": 1, - "payments": 1, - "personal": 1, - "profile.info": 1, - "reason": 1, - "refund.demanding": 1, - "refund.status": 1, - "thanks": 1, - "track": 1, - "user.response": 1, - "voucher": 1 - }, - "intentFeatures": { - "cancel": { - "can": 1, - "i": 1, - "cancel": 1, - "my": 1, - "order": 1, - "whi": 1, - "is": 1, - "how": 1, - "do": 1, - "an": 1 - }, - "confirm": { - "my": 1, - "payment": 1, - "was": 1, - "process": 1, - "success": 1, - "but": 1, - "i": 1, - "did": 1, - "not": 1, - "get": 1, - "ani": 1, - "order": 1, - "confirm": 1, - "what": 1, - "should": 1, - "do": 1, - "complet": 1, - "no": 1, - "money": 1, - "deduct": 1, - "is": 1 - }, - "delay": { - "whi": 1, - "is": 1, - "my": 1, - "order": 1, - "delay": 1, - "did": 1, - "get": 1, - "not": 1, - "deliv": 1, - "yet": 1, - "when": 1, - "do": 1, - "i": 1, - "deliveri": 1 - }, - "delivery": { - "how": 1, - "long": 1, - "doe": 1, - "deliveri": 1, - "take": 1, - "ship": 1, - "pleas": 1, - "tell": 1, - "me": 1, - "about": 1, - "my": 1, - "when": 1, - "do": 1, - "i": 1, - "get": 1, - "whi": 1, - "is": 1, - "order": 1, - "not": 1, - "deliv": 1, - "yet": 1 - }, - "greetings.bye": { - "goodby": 1, - "bye": 1, - "take": 1, - "care": 1, - "see": 1, - "you": 1, - "later": 1, - "for": 1, - "now": 1, - "i": 1, - "must": 1, - "go": 1 - }, - "greetings.hello": { - "hello": 1, - "hi": 1, - "howdi": 1, - "greet": 1, - "is": 1, - "anyon": 1, - "there": 1, - "good": 1, - "day": 1 - }, - "items": { - "which": 1, - "item": 1, - "do": 1, - "you": 1, - "have": 1, - "what": 1, - "kind": 1, - "of": 1, - "are": 1, - "there": 1, - "sell": 1, - "offer": 1, - "can": 1, - "i": 1, - "buy": 1, - "am": 1, - "look": 1, - "for": 1, - "ani": 1, - "interest": 1, - "in": 1, - "see": 1, - "want": 1, - "to": 1, - "a": 1, - "someth": 1, - "like": 1, - "this": 1, - "your": 1, - "most": 1, - "popular": 1, - "some": 1, - "best": 1, - "deal": 1, - "new": 1, - "arriv": 1 - }, - "more.contact": { - "need": 1, - "more": 1, - "help": 1, - "me": 1 - }, - "more.help": { - "can": 1, - "i": 1, - "talk": 1, - "to": 1, - "an": 1, - "agent": 1, - "call": 1, - "custom": 1, - "servic": 1, - "support": 1, - "number": 1, - "how": 1, - "contact": 1, - "for": 1, - "help": 1, - "helplin": 1, - "becom": 1, - "a": 1, - "seller": 1 - }, - "order": { - "what": 1, - "is": 1, - "my": 1, - "order": 1, - "status": 1, - "i": 1, - "want": 1, - "to": 1, - "know": 1, - "return": 1, - "how": 1 - }, - "payments": { - "do": 1, - "you": 1, - "take": 1, - "credit": 1, - "card": 1, - "accept": 1, - "mastercard": 1, - "can": 1, - "i": 1, - "pay": 1, - "with": 1, - "cash": 1, - "are": 1, - "onli": 1, - "what": 1, - "your": 1, - "payment": 1, - "method": 1, - "how": 1 - }, - "personal": { - "how": 1, - "are": 1, - "you": 1, - "do": 1, - "is": 1, - "your": 1, - "day": 1 - }, - "profile.info": { - "how": 1, - "can": 1, - "i": 1, - "chang": 1, - "my": 1, - "profil": 1, - "inform": 1, - "want": 1, - "to": 1, - "password": 1, - "phone": 1, - "number": 1, - "address": 1, - "reset": 1, - "delet": 1, - "account": 1 - }, - "reason": { - "common": 1, - "reason": 1, - "for": 1, - "deliveri": 1, - "delay": 1 - }, - "refund.demanding": { - "can": 1, - "i": 1, - "refund": 1, - "an": 1, - "item": 1, - "want": 1, - "to": 1, - "my": 1, - "order": 1, - "are": 1, - "avail": 1 - }, - "refund.status": { - "whi": 1, - "is": 1, - "the": 1, - "status": 1, - "refund": 1, - "when": 1, - "it": 1, - "not": 1, - "credit": 1, - "no": 1, - "even": 1, - "though": 1, - "say": 1, - "i": 1, - "did": 1, - "receiv": 1, - "my": 1, - "money": 1 - }, - "thanks": { - "thank": 1, - "you": 1, - "that": 1, - "is": 1, - "help": 1, - "a": 1, - "lot": 1, - "thx": 1, - "thnks": 1 - }, - "track": { - "how": 1, - "can": 1, - "i": 1, - "track": 1, - "my": 1, - "order": 1, - "want": 1, - "to": 1 - }, - "user.response": { - "i": 1, - "am": 1, - "good": 1, - "im": 1, - "do": 1, - "okay": 1 - }, - "voucher": { - "how": 1, - "to": 1, - "use": 1, - "a": 1, - "voucher": 1, - "can": 1, - "i": 1 - } - }, - "featuresToIntent": { - "can": [ - "cancel", - "items", - "more.help", - "payments", - "profile.info", - "refund.demanding", - "track", - "voucher" - ], - "i": [ - "cancel", - "confirm", - "delay", - "delivery", - "greetings.bye", - "items", - "more.help", - "order", - "payments", - "profile.info", - "refund.demanding", - "refund.status", - "track", - "user.response", - "voucher" - ], - "cancel": [ - "cancel" - ], - "my": [ - "cancel", - "confirm", - "delay", - "delivery", - "order", - "profile.info", - "refund.demanding", - "refund.status", - "track" - ], - "order": [ - "cancel", - "confirm", - "delay", - "delivery", - "order", - "refund.demanding", - "track" - ], - "whi": [ - "cancel", - "delay", - "delivery", - "refund.status" - ], - "is": [ - "cancel", - "confirm", - "delay", - "delivery", - "greetings.hello", - "order", - "personal", - "refund.status", - "thanks" - ], - "how": [ - "cancel", - "delivery", - "more.help", - "order", - "payments", - "personal", - "profile.info", - "track", - "voucher" - ], - "do": [ - "cancel", - "confirm", - "delay", - "delivery", - "items", - "payments", - "personal", - "user.response" - ], - "an": [ - "cancel", - "more.help", - "refund.demanding" - ], - "payment": [ - "confirm", - "payments" - ], - "was": [ - "confirm" - ], - "process": [ - "confirm" - ], - "success": [ - "confirm" - ], - "but": [ - "confirm" - ], - "did": [ - "confirm", - "delay", - "refund.status" - ], - "not": [ - "confirm", - "delay", - "delivery", - "refund.status" - ], - "get": [ - "confirm", - "delay", - "delivery" - ], - "ani": [ - "confirm", - "items" - ], - "confirm": [ - "confirm" - ], - "what": [ - "confirm", - "items", - "order", - "payments" - ], - "should": [ - "confirm" - ], - "complet": [ - "confirm" - ], - "no": [ - "confirm", - "refund.status" - ], - "money": [ - "confirm", - "refund.status" - ], - "deduct": [ - "confirm" - ], - "delay": [ - "delay", - "reason" - ], - "deliv": [ - "delay", - "delivery" - ], - "yet": [ - "delay", - "delivery" - ], - "when": [ - "delay", - "delivery", - "refund.status" - ], - "deliveri": [ - "delay", - "delivery", - "reason" - ], - "long": [ - "delivery" - ], - "doe": [ - "delivery" - ], - "take": [ - "delivery", - "greetings.bye", - "payments" - ], - "ship": [ - "delivery" - ], - "pleas": [ - "delivery" - ], - "tell": [ - "delivery" - ], - "me": [ - "delivery", - "more.contact" - ], - "about": [ - "delivery" - ], - "goodby": [ - "greetings.bye" - ], - "bye": [ - "greetings.bye" - ], - "care": [ - "greetings.bye" - ], - "see": [ - "greetings.bye", - "items" - ], - "you": [ - "greetings.bye", - "items", - "payments", - "personal", - "thanks" - ], - "later": [ - "greetings.bye" - ], - "for": [ - "greetings.bye", - "items", - "more.help", - "reason" - ], - "now": [ - "greetings.bye" - ], - "must": [ - "greetings.bye" - ], - "go": [ - "greetings.bye" - ], - "hello": [ - "greetings.hello" - ], - "hi": [ - "greetings.hello" - ], - "howdi": [ - "greetings.hello" - ], - "greet": [ - "greetings.hello" - ], - "anyon": [ - "greetings.hello" - ], - "there": [ - "greetings.hello", - "items" - ], - "good": [ - "greetings.hello", - "user.response" - ], - "day": [ - "greetings.hello", - "personal" - ], - "which": [ - "items" - ], - "item": [ - "items", - "refund.demanding" - ], - "have": [ - "items" - ], - "kind": [ - "items" - ], - "of": [ - "items" - ], - "are": [ - "items", - "payments", - "personal", - "refund.demanding" - ], - "sell": [ - "items" - ], - "offer": [ - "items" - ], - "buy": [ - "items" - ], - "am": [ - "items", - "user.response" - ], - "look": [ - "items" - ], - "interest": [ - "items" - ], - "in": [ - "items" - ], - "want": [ - "items", - "order", - "profile.info", - "refund.demanding", - "track" - ], - "to": [ - "items", - "more.help", - "order", - "profile.info", - "refund.demanding", - "track", - "voucher" - ], - "a": [ - "items", - "more.help", - "thanks", - "voucher" - ], - "someth": [ - "items" - ], - "like": [ - "items" - ], - "this": [ - "items" - ], - "your": [ - "items", - "payments", - "personal" - ], - "most": [ - "items" - ], - "popular": [ - "items" - ], - "some": [ - "items" - ], - "best": [ - "items" - ], - "deal": [ - "items" - ], - "new": [ - "items" - ], - "arriv": [ - "items" - ], - "need": [ - "more.contact" - ], - "more": [ - "more.contact" - ], - "help": [ - "more.contact", - "more.help", - "thanks" - ], - "talk": [ - "more.help" - ], - "agent": [ - "more.help" - ], - "call": [ - "more.help" - ], - "custom": [ - "more.help" - ], - "servic": [ - "more.help" - ], - "support": [ - "more.help" - ], - "number": [ - "more.help", - "profile.info" - ], - "contact": [ - "more.help" - ], - "helplin": [ - "more.help" - ], - "becom": [ - "more.help" - ], - "seller": [ - "more.help" - ], - "status": [ - "order", - "refund.status" - ], - "know": [ - "order" - ], - "return": [ - "order" - ], - "credit": [ - "payments", - "refund.status" - ], - "card": [ - "payments" - ], - "accept": [ - "payments" - ], - "mastercard": [ - "payments" - ], - "pay": [ - "payments" - ], - "with": [ - "payments" - ], - "cash": [ - "payments" - ], - "onli": [ - "payments" - ], - "method": [ - "payments" - ], - "chang": [ - "profile.info" - ], - "profil": [ - "profile.info" - ], - "inform": [ - "profile.info" - ], - "password": [ - "profile.info" - ], - "phone": [ - "profile.info" - ], - "address": [ - "profile.info" - ], - "reset": [ - "profile.info" - ], - "delet": [ - "profile.info" - ], - "account": [ - "profile.info" - ], - "common": [ - "reason" - ], - "reason": [ - "reason" - ], - "refund": [ - "refund.demanding", - "refund.status" - ], - "avail": [ - "refund.demanding" - ], - "the": [ - "refund.status" - ], - "it": [ - "refund.status" - ], - "even": [ - "refund.status" - ], - "though": [ - "refund.status" - ], - "say": [ - "refund.status" - ], - "receiv": [ - "refund.status" - ], - "thank": [ - "thanks" - ], - "that": [ - "thanks" - ], - "lot": [ - "thanks" - ], - "thx": [ - "thanks" - ], - "thnks": [ - "thanks" - ], - "track": [ - "track" - ], - "im": [ - "user.response" - ], - "okay": [ - "user.response" - ], - "use": [ - "voucher" - ], - "voucher": [ - "voucher" - ] - }, - "neuralNetwork": { + "isEditing": false, + "className": "BrainNLU", + "classifier": { + "className": "BrainClassifier", "settings": { - "locale": "en", - "tag": "nlu-en", - "keepStopwords": true, - "nonefeatureValue": 1, - "nonedeltaMultiplier": 1.2, - "spellCheck": false, - "spellCheckDistance": 1, - "filterZeros": true, - "log": true + "language": "en", + "useNoneFeature": true, + "config": { + "activation": "leaky-relu", + "hiddenLayers": [], + "iterations": 20000, + "learningRate": 0.7, + "errorThresh": 0.00005, + "momentum": 0.5, + "deltaErrorThresh": 0.000001, + "maxDecimals": 9, + "leakyReluAlpha": 0.08, + "timeout": 120000 + } }, - "features": [ - "can", - "i", - "cancel", - "my", - "order", - "whi", - "is", - "how", - "do", - "an", - "payment", - "was", - "process", - "success", - "but", - "did", - "not", - "get", - "ani", - "confirm", - "what", - "should", - "complet", - "no", - "money", - "deduct", - "delay", - "deliv", - "yet", - "when", - "deliveri", - "long", - "doe", - "take", - "ship", - "pleas", - "tell", - "me", - "about", - "goodby", - "bye", - "care", - "see", - "you", - "later", - "for", - "now", - "must", - "go", - "hello", - "hi", - "howdi", - "greet", - "anyon", - "there", - "good", - "day", - "which", - "item", - "have", - "kind", - "of", - "are", - "sell", - "offer", - "buy", - "am", - "look", - "interest", - "in", - "want", - "to", - "a", - "someth", - "like", - "this", - "your", - "most", - "popular", - "some", - "best", - "deal", - "new", - "arriv", - "need", - "more", - "help", - "talk", - "agent", - "call", - "custom", - "servic", - "support", - "number", - "contact", - "helplin", - "becom", - "seller", - "status", - "know", - "return", - "credit", - "card", - "accept", - "mastercard", - "pay", - "with", - "cash", - "onli", - "method", - "chang", - "profil", - "inform", - "password", - "phone", - "address", - "reset", - "delet", - "account", - "common", - "reason", - "refund", - "avail", - "the", - "it", - "even", - "though", - "say", - "receiv", - "thank", - "that", - "lot", - "thx", - "thnks", - "track", - "im", - "okay", - "use", - "voucher" - ], - "intents": [ - "cancel", - "confirm", - "delay", - "delivery", - "greetings.bye", - "greetings.hello", - "items", - "more.contact", - "more.help", - "order", - "payments", - "personal", - "profile.info", - "reason", - "refund.demanding", - "refund.status", - "thanks", - "track", - "user.response", - "voucher" - ], - "perceptrons": [ - [ - 1.0137546062469482, - -0.5474245548248291, - 11.848198890686035, - 0.4021919369697571, - 2.41121768951416, - 0.7352370619773865, - -0.11675096303224564, - -0.531909167766571, - 0.6789054870605469, - 0.5686358213424683, - -0.43452420830726624, - -0.7262206673622131, - -0.287818044424057, - -0.287818044424057, - -0.7318246364593506, - -0.9134994149208069, - -1.9920411109924316, - -1.386992335319519, - -0.287818044424057, - -1.0195549726486206, - -1.6521244049072266, - -0.287818044424057, - -0.002797139110043645, - -0.002797139110043645, - -0.18727074563503265, - -0.18727074563503265, - -2.59305739402771, - -0.8779183626174927, - -0.8779183626174927, - -0.33857086300849915, - -0.5329172015190125, - -0.14780335128307343, - -0.14780335128307343, - -0.1599469631910324, - -0.0766788199543953, - -0.0694824680685997, - -0.0694824680685997, - -0.0694824680685997, - -0.0694824680685997, - -0.0069275954738259315, - -0.010616364888846874, - -0.003003086894750595, - -0.16376909613609314, - -0.31498128175735474, - -0.006510923616588116, - -0.13119056820869446, - -0.006111734081059694, - -0.11673133820295334, - -0.11673133820295334, - -0.008423068560659885, - -0.0037213850300759077, - -0.0035652429796755314, - -0.0034156523179262877, - -0.012222626246511936, - -0.012222626246511936, - -0.02360852062702179, - -0.015696197748184204, - -0.0112577760592103, - -0.2867816090583801, - -0.17207609117031097, - 0, - 0, - -0.050361473113298416, - 0, - 0, - -0.3207390010356903, - -0.21179741621017456, - -0.11934908479452133, - -0.060127075761556625, - -0.24743472039699554, - -0.027676893398165703, - -0.5639355182647705, - -0.049367498606443405, - -0.015957951545715332, - -0.015957951545715332, - -0.015957951545715332, - -0.011720363982021809, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.4059183895587921, - -0.4059183895587921, - -0.2879806160926819, - -0.3107443153858185, - -0.3107443153858185, - 0, - 0, - -0.020473212003707886, - 0, - -0.011515682563185692, - -0.025052422657608986, - -0.5015026330947876, - -0.0068354010581970215, - -0.017309928312897682, - 0, - 0, - 0, - 0, - -0.38837194442749023, - -0.22747227549552917, - -0.22747227549552917, - 0, - 0, - -0.48755571246147156, - -0.48755571246147156, - -0.48755571246147156, - 0, - 0, - 0, - 0, - -0.005079725757241249, - -0.005079725757241249, - 0, - 0, - -2.282877206802368, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -2.269744634628296, - -0.018390238285064697, - 0, - 0, - 0, - -0.9896306948005769 - ], - [ - -0.3291322588920593, - -0.7270809412002563, - -0.16681955754756927, - -1.7737600803375244, - 1.892374873161316, - -1.1385151147842407, - -0.004768540151417255, - -0.19638314843177795, - -0.29744264483451843, - -0.18516650795936584, - 0.7383124828338623, - 4.340796947479248, - 0.2160470336675644, - 0.2160470336675644, - 2.533582925796509, - -0.36327800154685974, - 2.891287326812744, - -0.25634461641311646, - 0.09664863348007202, - 5.821985244750977, - -0.5967516303062439, - 0.2160470336675644, - 0.8364499807357788, - 0.6777428388595581, - 0.332731157541275, - 1.4811530113220215, - -0.5748860836029053, - -0.7142410278320312, - -0.7142410278320312, - -0.22726833820343018, - -0.18083734810352325, - -0.10491679608821869, - -0.10491679608821869, - -0.2062942087650299, - -0.036959417164325714, - -0.01146351732313633, - -0.01146351732313633, - -0.029890920966863632, - -0.01146351732313633, - -0.0765857994556427, - -0.1386977881193161, - -0.056668512523174286, - -0.061468705534935, - -0.4443903863430023, - -0.061468705534935, - -0.15229962766170502, - -0.057634614408016205, - -0.07866756618022919, - -0.07866756618022919, - -0.1323222666978836, - -0.0604797825217247, - -0.05799480155110359, - -0.055611882358789444, - -0.05049731209874153, - -0.10385537892580032, - -0.04838823154568672, - -0.04838823154568672, - -0.05117446184158325, - -0.1468285769224167, - -0.1599263697862625, - -0.03525151312351227, - -0.06675173342227936, - -0.3849853277206421, - -0.06461493670940399, - -0.05490032583475113, - -0.11226759105920792, - -0.11075426638126373, - -0.059033848345279694, - -0.03388472646474838, - -0.03388472646474838, - -0.03679174557328224, - -0.08090842515230179, - -0.03815352916717529, - -0.01093367300927639, - -0.01093367300927639, - -0.01093367300927639, - -0.32491371035575867, - -0.021362071856856346, - -0.021362071856856346, - -0.01930716447532177, - -0.01930716447532177, - -0.01930716447532177, - -0.02836797386407852, - -0.02836797386407852, - -0.023965725675225258, - -0.04677771031856537, - -0.06024671345949173, - -0.012021305970847607, - -0.012021305970847607, - -0.009854178875684738, - -0.06307084113359451, - -0.03795885667204857, - -0.013223793357610703, - -0.0717272013425827, - -0.0205734483897686, - -0.017550142481923103, - -0.0036168168298900127, - -0.006382063962519169, - -0.2861216962337494, - 0, - -0.0005231029936112463, - -0.038572512567043304, - 0, - 0, - 0, - -0.019087273627519608, - -0.00501098670065403, - -0.00501098670065403, - 0, - -0.26335811614990234, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -1.4128700494766235, - -0.005007071420550346, - -0.038572512567043304, - -0.038572512567043304, - -0.04391763359308243, - -0.04391763359308243, - -0.062341995537281036, - -1.1483964920043945, - -0.0012407215544953942, - 0, - 0, - -0.001188663300126791, - -0.0011387893464416265, - -1.2280935049057007, - 0, - 0, - 0, - 0, - -0.6626121500146844 - ], - [ - -1.2319387197494507, - -0.21801558136940002, - -7.187047004699707, - 3.2004880905151367, - 1.9386931657791138, - 3.482285976409912, - 1.7419606447219849, - -0.49997925758361816, - 2.0536139011383057, - -0.010287845507264137, - -0.7569913864135742, - -0.7583239674568176, - -0.7569913864135742, - -0.7569913864135742, - -0.7569913864135742, - 1.2748510837554932, - -1.373896598815918, - 4.428487300872803, - -0.7913482785224915, - -0.7583239674568176, - -2.4707539081573486, - -0.7569913864135742, - 0, - 0, - -0.19782285392284393, - 0, - 6.592715263366699, - -0.01434242632240057, - -0.01434242632240057, - 2.600883960723877, - -3.6173911094665527, - -0.23327256739139557, - -0.23327256739139557, - -0.2857179045677185, - -0.04939230531454086, - -0.6831585168838501, - -0.6831585168838501, - -0.6831585168838501, - -0.6831585168838501, - -0.04469833895564079, - -0.05349443107843399, - -0.019467800855636597, - -0.02620924822986126, - -0.21258683502674103, - -0.02620924822986126, - -1.9369317293167114, - -0.024292727932333946, - -0.04075866937637329, - -0.04075866937637329, - -0.06484147161245346, - -0.03467138111591339, - -0.03325444832444191, - -0.031895387917757034, - -0.13987219333648682, - -0.16497263312339783, - -0.02490754798054695, - -0.02490754798054695, - -0.031334955245256424, - -0.07237178832292557, - -0.07801632583141327, - -0.011419232003390789, - -0.01838001422584057, - -0.021844979375600815, - -0.024473845958709717, - -0.017832733690738678, - -0.04915530979633331, - -0.07258743047714233, - -0.040489088743925095, - -0.017853913828730583, - -0.017853913828730583, - -0.7192970514297485, - -0.7398727536201477, - -0.015225691720843315, - -0.008584280498325825, - -0.008584280498325825, - -0.008584280498325825, - -0.0018613251158967614, - -0.0004067723057232797, - -0.0004067723057232797, - -0.0012511666864156723, - -0.0012511666864156723, - -0.0012511666864156723, - -0.005174988880753517, - -0.005174988880753517, - -0.012096227146685123, - -0.012096227146685123, - -0.016754278913140297, - -0.010287845507264137, - -0.010287845507264137, - -0.008589562959969044, - -0.029535546898841858, - -0.017882922664284706, - -0.006336822174489498, - -0.046847932040691376, - -0.0018024882301688194, - -0.005290467292070389, - 0, - 0, - -1.9490653276443481, - -0.061430059373378754, - -0.061430059373378754, - -0.2684548497200012, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.20504964888095856, - -0.07669688761234283, - -0.07669688761234283, - -0.08714839071035385, - -0.014107950031757355, - -0.003805781714618206, - -0.012716507539153099, - -0.5159547328948975, - -0.5159547328948975, - -0.6215592622756958, - -1.8250330686569214, - -1.2078466415405273, - 0, - -0.2684548497200012, - -0.2684548497200012, - 0, - 0, - 0, - -0.19782285392284393, - 0, - 0, - 0, - 0, - 0, - -0.9307127594947815, - 0, - 0, - 0, - 0, - -2.8474070106071645 - ], - [ - -1.4451781511306763, - -1.0388836860656738, - -1.4264543056488037, - 2.5314676761627197, - -0.6839140057563782, - 0.7223742604255676, - -0.3522108495235443, - 1.6454493999481201, - 0.5822571516036987, - -0.03301115334033966, - -0.31011736392974854, - -0.31032857298851013, - -0.31011736392974854, - -0.31011736392974854, - -0.32225391268730164, - -0.8661167025566101, - 1.6880625486373901, - 1.8911114931106567, - -0.43638893961906433, - -0.32240062952041626, - -1.270650029182434, - -0.31011736392974854, - 0, - 0, - -0.354684978723526, - -0.006027175113558769, - -2.6551761627197266, - 2.678882122039795, - 2.678882122039795, - 2.1090545654296875, - 3.617849349975586, - 4.080210208892822, - 4.080210208892822, - 2.783381223678589, - 3.421110153198242, - 2.7887074947357178, - 2.7887074947357178, - 2.250455617904663, - 2.7887074947357178, - -0.17000629007816315, - -1.0011149644851685, - -0.852727472782135, - -0.12743763625621796, - -1.3179848194122314, - -0.08253855258226395, - -1.0258104801177979, - -0.07425964623689651, - -0.09686865657567978, - -0.09686865657567978, - -0.28232651948928833, - -0.13063865900039673, - -0.12527960538864136, - -0.12014026939868927, - -0.14789435267448425, - -0.23303095996379852, - -0.09936640411615372, - -0.4535828232765198, - -0.07006469368934631, - -0.19244204461574554, - -0.23298411071300507, - -0.04336852952837944, - -0.07129500061273575, - -0.3177240490913391, - -0.0663289874792099, - -0.05570872128009796, - -0.14094312489032745, - -0.23260855674743652, - -0.1286415457725525, - -0.05846906080842018, - -0.09133340418338776, - -0.5046393275260925, - -1.0630557537078857, - -0.34214919805526733, - -0.031061027199029922, - -0.031061027199029922, - -0.031061027199029922, - -0.3316175937652588, - -0.004776031244546175, - -0.004776031244546175, - -0.006242210045456886, - -0.006242210045456886, - -0.006242210045456886, - -0.019280698150396347, - -0.019280698150396347, - -0.05666345730423927, - -0.6110260486602783, - -0.6137350797653198, - -0.03301115334033966, - -0.03301115334033966, - -0.02883484587073326, - -0.23428231477737427, - -0.16950009763240814, - -0.0407194085419178, - -0.15757475793361664, - -0.21978570520877838, - -0.03889086842536926, - -0.11133536696434021, - -0.24443987011909485, - -1.0306564569473267, - -0.10143721848726273, - -0.20791855454444885, - -0.7177991271018982, - -0.3758620321750641, - 0, - 0, - -0.23245687782764435, - 0, - 0, - 0, - 0, - -0.5588673949241638, - -0.39160844683647156, - -0.39160844683647156, - -0.11280962079763412, - -0.01937008835375309, - -0.006116317119449377, - -0.02907666377723217, - -0.7320131063461304, - -0.7320131063461304, - -0.6825774312019348, - -0.6825774312019348, - -0.803561806678772, - 0, - -0.29712578654289246, - -0.29712578654289246, - 0, - 0, - 0, - -0.3424622714519501, - 0, - 0, - 0, - 0, - 0, - -0.40630239248275757, - 0, - 0, - 0, - 0, - -1.0805806744340831 - ], - [ - -2.1148478984832764, - 0.7997274994850159, - 0, - -0.8561583161354065, - -0.9215051531791687, - -0.006411668844521046, - -0.6620265245437622, - -1.2655911445617676, - -2.195646286010742, - -0.1171831414103508, - -0.22510455548763275, - -0.32071661949157715, - 0, - 0, - -0.22510455548763275, - -0.1251218467950821, - -0.439662367105484, - 0, - -0.23576650023460388, - -0.32071661949157715, - -2.3462860584259033, - 0, - -0.22510455548763275, - -0.4320996403694153, - -0.3351232707500458, - 0, - -1.3317266702651978, - 0, - 0, - -0.18428704142570496, - -0.7287682294845581, - -0.8036311864852905, - -0.8036311864852905, - 1.5954744815826416, - -0.4747324287891388, - 0, - 0, - -0.22864001989364624, - 0, - 12.440009117126465, - 7.522450923919678, - 3.414806365966797, - 5.261117458343506, - 0.5175203084945679, - 6.749449253082275, - 0.9009185433387756, - 4.107618808746338, - 5.865237712860107, - 5.865237712860107, - -1.7676278352737427, - -1.747532844543457, - -1.7450001239776611, - -1.7422480583190918, - -0.5406187176704407, - -0.6789042353630066, - -1.2395411729812622, - -0.739136278629303, - -0.18670904636383057, - -0.3578399419784546, - -2.0377581119537354, - -0.09841568022966385, - -0.12081167101860046, - -1.1265017986297607, - -0.22349581122398376, - -0.1958358734846115, - -0.5817500352859497, - -2.1600258350372314, - -1.4215301275253296, - -0.296112596988678, - -1.8322443962097168, - -0.6588131785392761, - -0.9206690192222595, - -0.4332333505153656, - -0.3052285313606262, - -0.3052285313606262, - -0.3052285313606262, - -0.02176925726234913, - 0, - 0, - -0.0050658793188631535, - -0.0050658793188631535, - -0.0050658793188631535, - -0.029020555317401886, - -0.029020555317401886, - -0.27671024203300476, - -0.533606231212616, - -1.04434072971344, - -0.1171831414103508, - -0.1171831414103508, - -0.13155850768089294, - -0.5340460538864136, - -0.3047577440738678, - -0.2095731496810913, - -1.2781013250350952, - -0.493476003408432, - -0.4744429588317871, - -0.0272076353430748, - -0.03713247552514076, - -0.35035839676856995, - -0.09408554434776306, - -0.12777751684188843, - -1.0055477619171143, - -0.9965164065361023, - -0.12271378934383392, - -0.12271378934383392, - -0.12974151968955994, - -0.12974151968955994, - -0.7104417681694031, - -0.5687134265899658, - 0, - -0.1271994262933731, - 0, - 0, - -0.11704851686954498, - 0, - -0.05229974165558815, - -0.04225518926978111, - -0.4482121765613556, - -0.4482121765613556, - -0.39033257961273193, - -1.3211712837219238, - -0.6426398158073425, - -0.08353639394044876, - -0.006411668844521046, - -0.006411668844521046, - -0.02102518081665039, - -0.02102518081665039, - -0.17535850405693054, - -0.3351232707500458, - -2.2644107341766357, - -0.04576003551483154, - 0, - -1.7263158559799194, - -1.72139310836792, - -0.8258929252624512, - -0.507616400718689, - -0.38756808638572693, - -0.00036949245259165764, - -0.00036949245259165764, - 1.7494741355394363 - ], - [ - -0.7732609510421753, - -4.034710884094238, - -0.16621102392673492, - -1.6832696199417114, - -2.2922849655151367, - -0.43388643860816956, - -1.0391435623168945, - -5.290063381195068, - -2.185589551925659, - -0.12314745038747787, - -0.5387074947357178, - -0.6298438906669617, - 0, - 0, - -0.5978250503540039, - 0, - -1.0414392948150635, - 0, - -0.31424424052238464, - -0.6889615654945374, - -2.4489927291870117, - 0, - -0.5387074947357178, - -1.0135843753814697, - -0.6875489354133606, - -0.04868263751268387, - -2.267845630645752, - 0, - 0, - -0.3452916443347931, - -1.842872977256775, - -0.07328616827726364, - -0.07328616827726364, - -1.2708898782730103, - -0.07328616827726364, - -0.01949692890048027, - -0.01949692890048027, - -0.638847827911377, - -0.01949692890048027, - -3.9341750144958496, - -1.613313913345337, - -1.1190882921218872, - -0.738145649433136, - -2.546537160873413, - -0.717369019985199, - -1.8951687812805176, - -0.4939974844455719, - -0.11324970424175262, - -0.11324970424175262, - 10.278204917907715, - 10.237865447998047, - 10.222160339355469, - 10.205157279968262, - 6.274806022644043, - 4.996850490570068, - 2.905292510986328, - 7.114895343780518, - -0.2540985643863678, - -1.758578896522522, - -0.7043660879135132, - -1.2804059982299805, - -1.3918099403381348, - -2.1373608112335205, - -0.1865386962890625, - -0.13705794513225555, - -0.44377002120018005, - -2.8761284351348877, - -0.4335267245769501, - -0.3696480691432953, - -0.3948061168193817, - -0.19673971831798553, - -1.0657670497894287, - -1.130800485610962, - -0.09097345173358917, - -0.09097345173358917, - -0.09097345173358917, - -4.737697601318359, - -0.07488127052783966, - -0.07488127052783966, - -0.07511845231056213, - -0.07511845231056213, - -0.07511845231056213, - -0.07152976095676422, - -0.07152976095676422, - -0.7411147356033325, - -1.3895823955535889, - -2.6020333766937256, - -0.12314745038747787, - -0.12314745038747787, - -0.10397472232580185, - -1.4408961534500122, - -0.725871205329895, - -0.6670722365379333, - -2.7231180667877197, - -0.3968248665332794, - -1.4507756233215332, - -0.20117171108722687, - -0.30739134550094604, - -1.123271107673645, - 0, - -0.1443576067686081, - -0.3079591393470764, - -0.05154714360833168, - -0.1468522697687149, - -0.1468522697687149, - -0.2189943641424179, - -0.2189943641424179, - -0.4288059175014496, - -0.17068028450012207, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -1.2483890056610107, - -1.2483890056610107, - -0.32805249094963074, - -0.7630698680877686, - -1.8030940294265747, - -0.4283504784107208, - -0.21113920211791992, - -0.21113920211791992, - -0.34558773040771484, - -0.34558773040771484, - -0.08313897997140884, - -0.6284115314483643, - -4.005587577819824, - -1.0675207376480103, - -0.5681915879249573, - -3.989133358001709, - -3.9799017906188965, - -1.730439305305481, - -6.760979175567627, - 0, - 0, - 0, - 4.00835038226357 - ], - [ - -2.0729150772094727, - 1.2105480432510376, - 0, - -3.075517416000366, - -2.1280879974365234, - 0, - -1.4291672706604004, - -3.7731683254241943, - 3.750875234603882, - -3.4637320041656494, - -3.808359146118164, - -0.7863858938217163, - -0.7863858938217163, - -0.7863858938217163, - -0.7863858938217163, - -0.7872745394706726, - -0.7872745394706726, - -0.8203457593917847, - 4.417398929595947, - -0.7863858938217163, - 7.2096943855285645, - -0.7863858938217163, - 0, - 0, - -0.0004443310899659991, - 0, - -1.4016138315200806, - 0, - 0, - -0.0339190810918808, - -0.5746974349021912, - 0, - 0, - -1.8838376998901367, - 0, - 0, - 0, - -0.29276347160339355, - 0, - 0, - -1.3986157178878784, - 0, - -2.1145453453063965, - 1.3252555131912231, - -0.3257300853729248, - 3.266697406768799, - -1.3986157178878784, - -0.37427330017089844, - -0.37427330017089844, - 0, - 0, - 0, - 0, - 0, - 1.3228527307510376, - -4.476612567901611, - 0, - 1.6962932348251343, - 2.854417085647583, - 5.147310733795166, - 1.3228527307510376, - 2.871696710586548, - -0.8401740193367004, - 2.4716386795043945, - 2.4736406803131104, - 8.386700630187988, - 3.6803982257843018, - 6.546629428863525, - 5.792974948883057, - 4.042235851287842, - 1.9097124338150024, - 0.2351648062467575, - 2.9972054958343506, - 0.014523402787744999, - 0.014523402787744999, - 0.014523402787744999, - 0.8547791838645935, - 2.3277828693389893, - 2.3277828693389893, - 1.5485361814498901, - 1.5485361814498901, - 1.5485361814498901, - 0.06981951743364334, - 0.06981951743364334, - -0.3465847671031952, - -0.7826536297798157, - -1.29860520362854, - -0.6727563142776489, - -0.6727563142776489, - -0.7746207118034363, - -1.5045040845870972, - -1.1596300601959229, - -0.21376065909862518, - -1.2060894966125488, - -0.7638314962387085, - -0.2846128046512604, - -0.21201957762241364, - -0.380899578332901, - -1.7838538885116577, - -0.3531716465950012, - -0.4074578583240509, - -1.8838376998901367, - -1.8838376998901367, - -2.419515371322632, - -2.419515371322632, - -1.6732022762298584, - -0.5018000602722168, - -1.5228439569473267, - -0.8719638586044312, - -3.0196237564086914, - -0.23078517615795135, - 0, - 0, - -0.19459597766399384, - 0, - -0.06361047178506851, - -0.032615043222904205, - -0.06359861046075821, - -0.06359861046075821, - -0.5170090794563293, - -1.4016138315200806, - -2.525960922241211, - -0.01885991171002388, - 0, - 0, - 0, - 0, - 0, - -0.0004443310899659991, - -1.074087142944336, - 0, - -0.08746566623449326, - -0.0025967436376959085, - -0.002491012681275606, - 0, - -0.1930888444185257, - -4.403278350830078, - -0.8553791642189026, - -0.8553791642189026, - -0.4586347245196692 - ], - [ - -0.22140660881996155, - -0.2870965600013733, - 0, - -0.7846165299415588, - -0.15873953700065613, - -0.011281087063252926, - -2.0644774436950684, - -0.23955339193344116, - -0.19472968578338623, - -0.08523216098546982, - -0.10342646390199661, - -0.05132650211453438, - 0, - 0, - -0.025844065472483635, - -0.00017023066175170243, - -0.11176835745573044, - 0, - 0, - -0.05132650211453438, - -0.12461326271295547, - 0, - -0.025844065472483635, - -0.08902999758720398, - -0.06705322861671448, - 0, - -0.15028099715709686, - 0, - 0, - -0.0490955151617527, - -0.745243489742279, - -0.0033512338995933533, - -0.0033512338995933533, - -0.12648093700408936, - -0.0033512338995933533, - -0.6030545830726624, - -0.6030545830726624, - 3.2248520851135254, - -0.6030545830726624, - -0.12146465480327606, - -0.03870086744427681, - -0.03870086744427681, - 0, - -0.30469033122062683, - 0, - -1.3063645362854004, - 0, - 0, - 0, - -0.137643501162529, - -0.11902635544538498, - -0.1179807186126709, - -0.11715350300073624, - 0, - 0, - -0.1290770322084427, - -0.05281056836247444, - 0, - -0.002628645393997431, - 0, - 0, - 0, - -0.23906093835830688, - 0, - 0, - 0, - -0.0207328200340271, - 0, - 0, - 0, - -0.014595159329473972, - -0.27468565106391907, - -0.07625328004360199, - 0, - 0, - 0, - -0.095185287296772, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 3.235283613204956, - 7.06290864944458, - 3.877683401107788, - -0.06153886765241623, - -0.06153886765241623, - -0.05395161360502243, - -0.2852313220500946, - -0.17201417684555054, - -0.08172166347503662, - -1.455437421798706, - -1.2979955673217773, - -0.053663939237594604, - -0.051299452781677246, - -0.07426265627145767, - -0.18414506316184998, - -0.014595159329473972, - -0.038418881595134735, - -0.08742590248584747, - -0.06441590934991837, - -0.0632362961769104, - -0.0632362961769104, - -0.052749164402484894, - -0.03576807305216789, - -0.10305000096559525, - -0.0530945248901844, - -0.06034179404377937, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.0791553482413292, - -0.0791553482413292, - -0.04793284460902214, - -0.06975246965885162, - -0.23104454576969147, - -0.0613517090678215, - -0.011281087063252926, - -0.011281087063252926, - -0.011696902103722095, - -0.011696902103722095, - -0.03297434374690056, - -0.06705322861671448, - -0.21499337255954742, - -1.9282985925674438, - -0.0005038426024839282, - -0.14002913236618042, - -0.13676577806472778, - -0.0662107840180397, - -0.08920516818761826, - -0.009708352386951447, - 0, - 0, - 0.11517137685731398 - ], - [ - 3.200519561767578, - -0.7589099407196045, - -1.5275399684906006, - -3.0995192527770996, - -2.00679612159729, - 0, - -1.429031491279602, - -0.012760818004608154, - -1.0567845106124878, - 0.7500213384628296, - -0.299394428730011, - -0.07060258835554123, - 0, - 0, - -0.0594392791390419, - 0, - -0.12525728344917297, - 0, - 0, - -0.07060258835554123, - -1.4175167083740234, - 0, - -0.0594392791390419, - -0.0651191994547844, - -0.11094554513692856, - 0, - -0.9104652404785156, - 0, - 0, - -0.00293157109990716, - -0.5573859810829163, - -0.08501628041267395, - -0.08501628041267395, - -0.3801807165145874, - -0.05955946817994118, - 0, - 0, - -0.552058219909668, - 0, - -0.3559640347957611, - -0.7074311971664429, - -0.023821957409381866, - -0.042731598019599915, - -1.234588623046875, - 0, - 1.0346851348876953, - -0.6757804155349731, - 0, - 0, - -0.38050055503845215, - -0.3475361168384552, - -0.3460211753845215, - -0.3452502191066742, - 0, - 0, - -0.3270740211009979, - -0.35466599464416504, - 0, - -1.6785608530044556, - -0.042731598019599915, - 0, - 0, - -0.8738346695899963, - 0, - 0, - -1.3493611812591553, - -0.33822640776634216, - -0.2678961753845215, - 0, - -0.042731598019599915, - -3.1001944541931152, - 2.860347270965576, - 1.7957031726837158, - -0.018584920093417168, - -0.018584920093417168, - -0.018584920093417168, - -0.5774023532867432, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.5536002516746521, - -1.1061232089996338, - 1.3213735818862915, - 3.957881212234497, - 3.957881212234497, - 4.638659954071045, - 4.42684268951416, - 2.3681249618530273, - 2.0528454780578613, - 7.3816752433776855, - 4.17827033996582, - 6.468132495880127, - 4.017130374908447, - 5.217040538787842, - -1.9796524047851562, - -0.3908320665359497, - -1.6709413528442383, - -0.19154834747314453, - -0.19154834747314453, - -0.17416070401668549, - -0.17416070401668549, - -1.2428146600723267, - -0.7238667011260986, - -0.8973953723907471, - -0.0867948979139328, - -0.16916145384311676, - -2.2424511909484863, - -0.32075998187065125, - -0.32075998187065125, - -0.2476712465286255, - -1.478501558303833, - 0, - -0.006954232230782509, - 0, - 0, - -0.3725646734237671, - -0.7989145517349243, - -1.854297399520874, - -0.016981689259409904, - 0, - 0, - 0, - 0, - -0.00293157109990716, - -0.11094554513692856, - -0.6883361339569092, - -0.3473883867263794, - -0.3317181169986725, - -0.4416748285293579, - -0.43391868472099304, - -0.1418841928243637, - -0.27213436365127563, - -0.021505234763026237, - -2.5270771980285645, - -2.5270771980285645, - 0.3450876493306147 - ], - [ - -0.6420600414276123, - -1.9181957244873047, - -0.9622277617454529, - 1.3733851909637451, - 1.7130494117736816, - -2.1014411449432373, - 0.9902098774909973, - 0.909596860408783, - -1.1616764068603516, - -0.0607258602976799, - -0.4026883542537689, - -0.10765376687049866, - -0.0967993512749672, - -0.0967993512749672, - -0.2516874670982361, - -0.5531376600265503, - -0.9255282282829285, - -0.5531376600265503, - -0.0967993512749672, - -0.2632655203342438, - 3.4905331134796143, - -0.0967993512749672, - 0, - -2.0475926399230957, - -0.12525439262390137, - -0.12525439262390137, - -0.9677612781524658, - -0.021905967965722084, - -0.021905967965722084, - -1.255740761756897, - -0.11220162361860275, - 0, - 0, - -0.04746983200311661, - 0, - -0.07886837422847748, - -0.07886837422847748, - -0.07886837422847748, - -0.07886837422847748, - 0, - 0, - 0, - 0, - -1.0360527038574219, - 0, - -0.02445678412914276, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.28641000390052795, - 0, - -0.21070648729801178, - 0, - -0.47402000427246094, - 0, - -0.28641000390052795, - -0.3845341205596924, - -1.04691481590271, - -0.2858976721763611, - -0.28032997250556946, - 0, - 0, - 0, - 0, - 0, - -0.9096251130104065, - 1.1884770393371582, - -0.6546885967254639, - 0, - 0, - 0, - -0.7524798512458801, - -0.10243398696184158, - -0.10243398696184158, - -0.09811604022979736, - -0.09811604022979736, - -0.09811604022979736, - 0, - 0, - 0, - 0, - -0.015519799664616585, - 0, - 0, - 0, - -0.3532436788082123, - -0.3532436788082123, - 0, - -0.3316343426704407, - -0.3924787938594818, - 0, - -0.2033385932445526, - -0.24257385730743408, - 7.729468822479248, - 2.36399245262146, - 5.470393657684326, - -0.6171549558639526, - -0.04746983200311661, - -0.06629005819559097, - -0.06629005819559097, - -0.2993519902229309, - -0.10318173468112946, - -0.1877218633890152, - -0.05038710683584213, - -0.2762553095817566, - -1.657719612121582, - -0.20799502730369568, - -0.20799502730369568, - -0.8902863264083862, - -0.3316343426704407, - -0.32283616065979004, - -0.3706023395061493, - -0.5664393901824951, - -0.5664393901824951, - -0.015637066215276718, - -0.02445678412914276, - -2.798739433288574, - 0, - -0.5251821875572205, - -0.5251821875572205, - -1.4305318593978882, - -1.4305318593978882, - -0.6157680153846741, - 0, - 0, - -0.015519799664616585, - 0, - 0, - 0, - -1.0123201608657837, - 0, - 0, - -0.39987918734550476, - -0.39987918734550476, - -1.012125551662553 - ], - [ - -3.8618507385253906, - 2.661921739578247, - -0.324486643075943, - -2.681408166885376, - -1.070864200592041, - -0.1429082453250885, - -0.9401305913925171, - -2.0808780193328857, - 3.4362409114837646, - -0.5510810613632202, - 5.012321949005127, - -1.0682185888290405, - -0.24958382546901703, - -0.24958382546901703, - -1.0682185888290405, - -0.24958382546901703, - -0.5058361291885376, - -1.057460904121399, - -1.2256693840026855, - -1.0682185888290405, - -0.5766453146934509, - -0.24958382546901703, - -0.7393555045127869, - -0.7923861742019653, - -0.02366284839808941, - 0, - -0.5207908749580383, - 0, - 0, - -0.9092050790786743, - -1.1664316654205322, - -0.23604610562324524, - -0.23604610562324524, - 2.949445962905884, - -0.22940532863140106, - 0, - 0, - 0, - 0, - -0.10126614570617676, - -1.332739233970642, - -1.332739233970642, - -0.5901528000831604, - -0.7608097195625305, - -0.5048365592956543, - -0.5548068881034851, - 0, - -1.1728324890136719, - -1.1728324890136719, - -0.06950733810663223, - -0.04147882014513016, - -0.03993690013885498, - -0.03845171630382538, - 0, - -0.10618571937084198, - -1.829228401184082, - -0.6239013671875, - -0.7514669299125671, - -2.0999088287353516, - -1.7867448329925537, - -0.10618571937084198, - -0.9889819622039795, - 0.08560609817504883, - -2.1254465579986572, - -1.9958056211471558, - -0.3938402533531189, - -1.6643561124801636, - -0.16029290854930878, - -0.31657809019088745, - -0.39398059248924255, - -0.7145650386810303, - -0.7145650386810303, - -0.6206731796264648, - -0.007626057136803865, - -0.007626057136803865, - -0.007626057136803865, - 3.9068644046783447, - -0.7097881436347961, - -0.7097881436347961, - -0.8330930471420288, - -0.8330930471420288, - -0.8330930471420288, - -0.13839125633239746, - -0.13839125633239746, - 0, - 0, - -0.08971934020519257, - 0, - 0, - -0.2813188433647156, - -0.2813188433647156, - -0.2813188433647156, - 0, - -0.09263147413730621, - 0, - 0, - 0, - 0, - -0.19597065448760986, - 0, - 0, - 4.378785133361816, - 4.526092529296875, - 5.922995090484619, - 5.922995090484619, - 10.107356071472168, - -1.0512547492980957, - 6.942415714263916, - 7.998384952545166, - 6.079689025878906, - -0.7038584351539612, - -0.15375125408172607, - -0.15375125408172607, - -0.23844508826732635, - -0.09263147413730621, - -0.05494779720902443, - -0.048433784395456314, - -0.09314513951539993, - -0.09314513951539993, - -0.23382462561130524, - -0.35334011912345886, - -1.2957956790924072, - -0.409479022026062, - -0.1429082453250885, - -0.1429082453250885, - -0.009866238571703434, - -0.009866238571703434, - -0.023210253566503525, - -0.02366284839808941, - -1.1075395345687866, - -0.08971934020519257, - -0.022661320865154266, - -0.2660955786705017, - -0.2567301392555237, - -0.18965840339660645, - -1.3401103019714355, - -0.6953500509262085, - -0.5650429129600525, - -0.5650429129600525, - -0.2684726967987179 - ], - [ - -1.0223896503448486, - -3.3254759311676025, - -0.3594515919685364, - -1.2034928798675537, - -0.7094810009002686, - -0.11835822463035583, - 3.11519455909729, - 7.990487098693848, - 0.09484227001667023, - -0.03216062858700752, - -0.9383915066719055, - 0, - 0, - 0, - -0.043270353227853775, - 0, - -0.18485020101070404, - 0, - -0.6258415579795837, - -0.043270353227853775, - -2.782179832458496, - 0, - 0, - -0.12773528695106506, - -0.06305236369371414, - -0.043270353227853775, - -0.24601295590400696, - 0, - 0, - -0.10957992821931839, - -0.8636319041252136, - -1.4621272087097168, - -1.4621272087097168, - -1.591508150100708, - -0.7966815233230591, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.6140099167823792, - 3.904226064682007, - -0.6140099167823792, - -0.17317022383213043, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.2878693640232086, - -0.3986928164958954, - -0.9750335216522217, - 3.525062322616577, - -0.35377830266952515, - -1.0733240842819214, - -1.0409214496612549, - -0.06699375808238983, - -0.6057316064834595, - 5.004830837249756, - -0.3178902864456177, - -0.25369635224342346, - 0, - 0, - 0, - 0, - 0, - -0.13478565216064453, - -3.095573663711548, - -1.419830322265625, - 0, - 0, - 0, - 2.3318679332733154, - -0.4985043406486511, - -0.4985043406486511, - -0.4953354299068451, - -0.4953354299068451, - -0.4953354299068451, - -0.16272452473640442, - -0.16272452473640442, - 0, - 0, - -0.37520232796669006, - 0, - 0, - 0, - -0.7256875038146973, - -0.7256875038146973, - 0, - -0.015781344845891, - -0.7694862484931946, - 0, - -0.377000093460083, - -0.4205278754234314, - -1.2554208040237427, - 0, - -0.9600287079811096, - -0.24299727380275726, - -0.11906661838293076, - -0.7379854321479797, - -0.7379854321479797, - -2.122434139251709, - 0, - -3.112035036087036, - -3.112035036087036, - -0.9383915066719055, - -0.4391774535179138, - -0.31684646010398865, - -0.31684646010398865, - -0.05564532428979874, - -0.015781344845891, - -0.005053800996392965, - -0.009785249829292297, - -0.03709046170115471, - -0.03709046170115471, - -0.09168491512537003, - -0.17317022383213043, - -1.3464040756225586, - -0.9806076288223267, - -0.08754246681928635, - -0.08754246681928635, - -0.10532320290803909, - -0.10532320290803909, - -0.006247076205909252, - -0.011304006911814213, - -1.222589373588562, - -0.37520232796669006, - -0.10687950998544693, - -0.0387471504509449, - -0.03715534880757332, - -0.22339007258415222, - -0.1271572709083557, - 0, - -0.8067957758903503, - -0.8067957758903503, - -2.6753750922540664 - ], - [ - -0.12718820571899414, - -0.9853842854499817, - -0.5445895195007324, - 5.641761779785156, - -4.2250847816467285, - -0.280537873506546, - -0.4256134331226349, - 0.3338262736797333, - -0.918182909488678, - -1.3661450147628784, - 0, - 0, - 0, - 0, - 0, - -0.6540312767028809, - -0.6540312767028809, - -0.8484894037246704, - 0, - 0, - 0, - 0, - 0, - 0, - -0.6540312767028809, - 0, - -0.5487508773803711, - 0, - 0, - -0.8484894037246704, - -2.1415629386901855, - -0.0575675331056118, - -0.0575675331056118, - -0.0575675331056118, - -0.0575675331056118, - -0.8333489298820496, - -0.8333489298820496, - -0.8333489298820496, - -0.8333489298820496, - 0, - 0, - 0, - 0, - -0.1419188380241394, - 0, - -0.4246017634868622, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.15350814163684845, - 0, - 0, - -1.3661450147628784, - 0, - 0, - 0, - -0.037509985268116, - 0, - 0, - -0.844822108745575, - -0.22552567720413208, - 0, - 0, - 0, - 1.1137510538101196, - 0.27898016571998596, - -1.4447450637817383, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.11407973617315292, - 0, - 0, - 0, - -0.5878565311431885, - -0.3544137477874756, - -0.21363283693790436, - 0.967481255531311, - -0.3053710162639618, - -0.7675119638442993, - -0.007795386016368866, - -0.007795386016368866, - -2.1931285858154297, - -2.1931285858154297, - -2.1931285858154297, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 5.353912830352783, - 2.1121280193328857, - 2.1121280193328857, - 3.23943829536438, - 2.038325786590576, - 2.9767329692840576, - 5.013604164123535, - 4.2997145652771, - 4.2997145652771, - -0.19218771159648895, - -0.3588467240333557, - -3.047213554382324, - 0, - 0, - 0, - 0, - 0, - 0, - -0.6540312767028809, - -0.250901460647583, - -0.08072638511657715, - -0.021549042314291, - -0.09487134218215942, - -0.09106424450874329, - -2.8172950744628906, - -0.0021756167989224195, - -0.04602548107504845, - -0.4669470489025116, - -0.4669470489025116, - -0.14674373041027708 - ], - [ - -0.23209355771541595, - -1.1615275144577026, - -0.005190588533878326, - -3.6277239322662354, - -3.169827938079834, - -1.4822739362716675, - -1.7275220155715942, - -1.2139179706573486, - -0.2021854668855667, - -0.1523972898721695, - -0.11314810812473297, - -0.08779977262020111, - 0, - 0, - -0.059583138674497604, - -1.5408085584640503, - -0.15550094842910767, - -1.6674295663833618, - 0, - -0.08779977262020111, - -0.05608239024877548, - 0, - -0.059583138674497604, - -0.16862013936042786, - -0.045018356293439865, - 0, - 9.606298446655273, - 0, - 0, - -0.24552878737449646, - 4.0383477210998535, - -1.0773080587387085, - -1.0773080587387085, - -1.092512607574463, - 0, - -0.3511105179786682, - -0.3511105179786682, - -0.3511105179786682, - -0.3511105179786682, - -0.4085167646408081, - -0.9077059030532837, - -0.007602308411151171, - -0.09288404881954193, - -0.23860332369804382, - -0.09288404881954193, - 1.0059305429458618, - -0.8925086259841919, - 0, - 0, - -0.41859158873558044, - -0.4048844575881958, - -0.4027630090713501, - -0.4005860686302185, - 0, - -0.0000060307379499136005, - -0.3426108956336975, - -0.0742335394024849, - 0, - -0.19533050060272217, - 0, - -0.0000060307379499136005, - -0.03409332409501076, - -0.1738254725933075, - 0, - 0, - 0, - -0.5821152925491333, - -0.4939543306827545, - 0, - 0, - -0.09052671492099762, - -0.20556989312171936, - -0.14422670006752014, - -0.15840737521648407, - -0.15840737521648407, - -0.15840737521648407, - -0.05607032775878906, - 0, - 0, - -0.03408125042915344, - -0.03408125042915344, - -0.03408125042915344, - 0, - 0, - -0.00014428634312935174, - -0.00014428634312935174, - -0.7335526943206787, - 0, - 0, - 0, - -0.004638887010514736, - -0.001708140131086111, - -0.001953831408172846, - -0.6299360990524292, - -0.6226332187652588, - 0, - 0, - 0, - -0.1530466079711914, - 0, - 0, - -0.04347405955195427, - 0, - 0, - 0, - 0, - 0, - -0.0043099685572087765, - -0.0043099685572087765, - -0.02191714569926262, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -3.760802745819092, - 3.084120035171509, - -0.6946653127670288, - -0.08140380680561066, - -0.04347405955195427, - -0.04347405955195427, - -0.034206993877887726, - -0.034206993877887726, - -0.02565651200711727, - -0.045018356293439865, - -0.42496785521507263, - -0.07680274546146393, - -0.033271674066782, - -0.4210048317909241, - -0.41981253027915955, - -0.11387768387794495, - -0.16938365995883942, - -0.012891450896859169, - -0.08184230327606201, - -0.08184230327606201, - 0.42938872349061835 + "labels": [], + "network": { + "sizes": [ + 137, + 21 ], - [ - 1.292214274406433, - 0.26029807329177856, - -3.265094041824341, - 1.323584794998169, - 2.0901131629943848, - -0.3446880578994751, - -1.5694751739501953, - -0.8493215441703796, - -0.5331605672836304, - 1.0180702209472656, - -0.0004536763299256563, - 0, - 0, - 0, - 0, - -1.6404364109039307, - -3.4016611576080322, - -0.037170104682445526, - 0, - 0, - -1.677952527999878, - 0, - 0, - -2.535099506378174, - -2.8807036876678467, - 0, - -0.037170104682445526, - 0, - 0, - -2.04948091506958, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.0057027386501431465, - -0.5997958779335022, - 0, - 0, - 0, - -0.05450645834207535, - -0.05450645834207535, - 0, - 0, - 0, - 0, - 0, - -0.7775706052780151, - -0.4098859131336212, - 0, - -0.4394400715827942, - 2.708770275115967, - -0.4485231637954712, - -0.7775706052780151, - -0.785632848739624, - 1.7545942068099976, - 0, - 0, - -0.398097425699234, - -0.5929336547851562, - 0, - 0, - -0.0057027386501431465, - 1.2487276792526245, - -0.1948939859867096, - -0.8948578238487244, - 0, - 0, - 0, - -0.7649872899055481, - -0.7560442090034485, - -0.7560442090034485, - -0.003941982984542847, - -0.003941982984542847, - -0.003941982984542847, - 0, - 0, - 0, - 0, - -0.015651213005185127, - -1.3101164102554321, - -1.3101164102554321, - -0.26000744104385376, - -0.26000744104385376, - -0.26000744104385376, - 0, - -0.13279704749584198, - 0, - 0, - 0, - 0, - -3.0560569763183594, - 0, - 0, - -0.3446880578994751, - 0, - 0, - 0, - -0.21462611854076385, - -0.21462611854076385, - -0.26094186305999756, - -0.039530932903289795, - -0.0004536763299256563, - -0.6876921057701111, - -0.10246691852807999, - -0.10246691852807999, - -0.5645003914833069, - -0.13279704749584198, - -0.25327029824256897, - -0.37001025676727295, - -0.4633078873157501, - -0.4633078873157501, - 0, - 0, - 10.846235275268555, - 3.3832929134368896, - -0.3446880578994751, - -0.3446880578994751, - -1.0060052871704102, - -1.0060052871704102, - -1.5285238027572632, - -2.8807036876678467, - -0.17504888772964478, - -0.015651213005185127, - -0.0056052375584840775, - -0.0669519379734993, - -0.06419989466667175, - -3.2434446811676025, - -0.012034664861857891, - -0.15987606346607208, - -0.5193120241165161, - -0.5193120241165161, - -1.6741036713965793 + "layers": [ + { + "can": {}, + "i": {}, + "cancel": {}, + "my": {}, + "order": {}, + "whi": {}, + "is": {}, + "how": {}, + "do": {}, + "an": {}, + "payment": {}, + "was": {}, + "process": {}, + "success": {}, + "but": {}, + "did": {}, + "not": {}, + "get": {}, + "ani": {}, + "confirm": {}, + "what": {}, + "should": {}, + "complet": {}, + "no": {}, + "money": {}, + "deduct": {}, + "delay": {}, + "long": {}, + "doe": {}, + "deliveri": {}, + "take": {}, + "ship": {}, + "pleas": {}, + "tell": {}, + "me": {}, + "about": {}, + "goodby": {}, + "bye": {}, + "care": {}, + "see": {}, + "you": {}, + "later": {}, + "for": {}, + "now": {}, + "must": {}, + "go": {}, + "hi": {}, + "howdi": {}, + "greet": {}, + "anyon": {}, + "there": {}, + "good": {}, + "day": {}, + "which": {}, + "item": {}, + "have": {}, + "kind": {}, + "of": {}, + "are": {}, + "sell": {}, + "offer": {}, + "buy": {}, + "am": {}, + "look": {}, + "interest": {}, + "in": {}, + "want": {}, + "to": {}, + "a": {}, + "someth": {}, + "like": {}, + "this": {}, + "your": {}, + "most": {}, + "popular": {}, + "some": {}, + "best": {}, + "deal": {}, + "new": {}, + "arriv": {}, + "need": {}, + "more": {}, + "help": {}, + "talk": {}, + "agent": {}, + "call": {}, + "custom": {}, + "servic": {}, + "support": {}, + "number": {}, + "contact": {}, + "helplin": {}, + "becom": {}, + "seller": {}, + "status": {}, + "know": {}, + "return": {}, + "credit": {}, + "card": {}, + "accept": {}, + "mastercard": {}, + "pay": {}, + "with": {}, + "cash": {}, + "onli": {}, + "method": {}, + "chang": {}, + "profil": {}, + "inform": {}, + "password": {}, + "phone": {}, + "address": {}, + "reset": {}, + "delet": {}, + "account": {}, + "common": {}, + "reason": {}, + "refund": {}, + "avail": {}, + "the": {}, + "when": {}, + "it": {}, + "even": {}, + "though": {}, + "say": {}, + "receiv": {}, + "thank": {}, + "that": {}, + "lot": {}, + "thx": {}, + "thnks": {}, + "track": {}, + "im": {}, + "okay": {}, + "use": {}, + "voucher": {}, + "nonefeature": {} + }, + { + "cancel": { + "bias": -0.930104211, + "weights": { + "can": 0.957473934, + "i": -0.525079012, + "cancel": 10.265582085, + "my": 0.371328503, + "order": 2.088700533, + "whi": 0.763850927, + "is": -0.068361253, + "how": -0.463389397, + "do": 0.687301219, + "an": 0.640718579, + "payment": -0.919768572, + "was": -1.166503429, + "process": -0.919768572, + "success": -0.919768572, + "but": -1.180019498, + "did": -1.157185674, + "not": -1.426754475, + "get": -1.157185674, + "ani": -0.919768572, + "confirm": -1.426754475, + "what": -1.940908074, + "should": -0.919768572, + "money": -0.260250956, + "deduct": -0.260250956, + "delay": -2.570578575, + "long": -0.286226302, + "doe": -0.286226302, + "deliveri": -0.375763714, + "take": -0.286226302, + "ship": -0.120106436, + "pleas": -0.209643885, + "tell": -0.209643885, + "me": -0.209643885, + "about": -0.209643885, + "goodby": -0.029091995, + "bye": -0.025927998, + "see": -0.198298454, + "you": -0.26647839, + "later": -0.027464474, + "for": -0.146737397, + "now": -0.025927998, + "must": -0.210782304, + "go": -0.210782304, + "hi": -0.016967077, + "howdi": -0.016017871, + "greet": -0.015121765, + "anyon": -0.06386508, + "there": -0.06386508, + "good": -0.01155964, + "day": -0.063141055, + "which": -0.021084057, + "item": -0.21557793, + "have": -0.191918015, + "are": -0.047095876, + "buy": -0.408729583, + "am": -0.206254885, + "look": -0.120809413, + "interest": -0.085445479, + "in": -0.256279439, + "want": -0.044856783, + "to": -0.623640776, + "a": -0.035365846, + "someth": -0.001406228, + "like": -0.001406228, + "this": -0.001406228, + "your": -0.051581416, + "talk": -0.556157231, + "agent": -0.556157231, + "call": -0.372072816, + "custom": -0.372072816, + "servic": -0.372072816, + "contact": -0.00603973, + "becom": -0.014387307, + "seller": -0.020427037, + "status": -0.48863253, + "know": -0.029917972, + "return": -0.032117736, + "pay": -0.358795494, + "with": -0.257904977, + "cash": -0.257904977, + "chang": -0.542808354, + "profil": -0.542808354, + "inform": -0.542808354, + "delet": -0.050617501, + "account": -0.050617501, + "refund": -1.975938201, + "track": -1.941594362, + "nonefeature": 2.6e-7 + } + }, + "confirm": { + "bias": -0.572734939, + "weights": { + "can": -0.302591026, + "i": -0.677182376, + "cancel": -0.159523576, + "my": -1.207372069, + "order": 1.71398139, + "whi": -0.600412011, + "is": 0.145629793, + "how": -0.263827533, + "do": -0.132612392, + "an": -0.171640351, + "payment": 0.750604868, + "was": 3.686539412, + "process": 0.117877729, + "success": 0.117877729, + "but": 2.186765194, + "did": -0.469934672, + "not": 2.786321878, + "get": -0.251185477, + "ani": 0.043543316, + "confirm": 4.866500378, + "what": -0.575253546, + "should": 0.117877729, + "complet": 0.888924956, + "no": 0.700297356, + "money": 0.19279699, + "deduct": 1.179962158, + "delay": -0.765385151, + "long": -0.257211715, + "doe": -0.257211715, + "deliveri": -0.211051494, + "take": -0.355303019, + "ship": -0.107694343, + "pleas": -0.061534096, + "tell": -0.061534096, + "me": -0.093213446, + "about": -0.061534096, + "goodby": -0.131685451, + "bye": -0.202042803, + "care": -0.098091289, + "see": -0.118830815, + "you": -0.369320899, + "later": -0.118830815, + "for": -0.154676482, + "now": -0.103951491, + "must": -0.118884884, + "go": -0.118884884, + "hi": -0.10159415, + "howdi": -0.096534215, + "greet": -0.091722503, + "anyon": -0.128030211, + "there": -0.192434385, + "good": -0.07673718, + "day": -0.07673718, + "which": -0.071719304, + "item": -0.149367779, + "have": -0.146053746, + "kind": -0.064404167, + "of": -0.082598262, + "are": -0.352040768, + "sell": -0.060944177, + "offer": -0.043492179, + "buy": -0.088249192, + "am": -0.084193207, + "look": -0.044874575, + "interest": -0.039318629, + "in": -0.039318629, + "want": -0.070641801, + "to": -0.083106577, + "a": -0.03136643, + "someth": -0.004657233, + "like": -0.004657233, + "this": -0.004657233, + "your": -0.287636608, + "most": -0.013244311, + "popular": -0.013244311, + "some": -0.018194089, + "best": -0.018194089, + "deal": -0.018194089, + "new": -0.023532076, + "arriv": -0.023532076, + "need": -0.046341963, + "more": -0.07802131, + "help": -0.083871759, + "talk": -0.012116769, + "agent": -0.012116769, + "call": -0.013533616, + "custom": -0.082597509, + "servic": -0.042812482, + "support": -0.03978502, + "number": -0.103989147, + "contact": -0.006198453, + "helplin": -0.029422821, + "status": -0.572488904, + "credit": -0.204090014, + "method": -0.256198198, + "refund": -1.417948127, + "the": -0.204090014, + "when": -0.26659891, + "it": -0.204090014, + "even": -0.126118794, + "though": -0.126118794, + "say": -0.062508918, + "receiv": -0.987164974, + "track": -1.120849967, + "nonefeature": 2.6e-7 + } + }, + "delay": { + "bias": -2.223512154, + "weights": { + "can": -0.692000508, + "i": -1.675773501, + "cancel": -5.26270771, + "my": 1.892218351, + "order": 2.450906038, + "whi": 2.658858776, + "is": 1.122332335, + "how": -0.362010807, + "do": -0.426054001, + "an": -0.006485965, + "payment": -0.358077705, + "was": -0.358077705, + "process": -0.358077705, + "success": -0.358077705, + "but": -0.358077705, + "did": 2.139068365, + "not": -0.558650017, + "get": 2.339640379, + "ani": -0.363735795, + "confirm": -0.358077705, + "what": -1.800407648, + "should": -0.358077705, + "money": -0.200572416, + "delay": 5.991657257, + "long": -0.142958999, + "doe": -0.142958999, + "deliveri": -3.50396347, + "take": -0.18796438, + "ship": -0.059856821, + "pleas": -0.259056717, + "tell": -0.259056717, + "me": -0.259056717, + "about": -0.259056717, + "goodby": -0.094268851, + "bye": -0.095100984, + "care": -0.045005374, + "see": -0.066799991, + "you": -0.134776279, + "later": -0.066799991, + "for": -1.920385003, + "now": -0.050095607, + "must": -0.051069729, + "go": -0.051069729, + "hi": -0.07874845, + "howdi": -0.074646123, + "greet": -0.070756368, + "anyon": -0.171468496, + "there": -0.198504359, + "good": -0.053611815, + "day": -0.073775604, + "which": -0.029132444, + "item": -0.064197257, + "have": -0.034790568, + "kind": -0.027035886, + "of": -0.036488183, + "are": -0.044517107, + "sell": -0.019569596, + "offer": -0.013616127, + "buy": -0.034369979, + "am": -0.036562871, + "look": -0.018932888, + "interest": -0.017629983, + "in": -0.017629983, + "want": -0.33749038, + "to": -0.345083058, + "a": -0.014635772, + "someth": -0.001962413, + "like": -0.001962413, + "this": -0.001962413, + "your": -0.037645005, + "most": -0.008028925, + "popular": -0.008028925, + "some": -0.009452293, + "best": -0.009452293, + "deal": -0.009452293, + "need": -0.039504211, + "more": -0.039504211, + "help": -0.046586424, + "talk": -0.006485965, + "agent": -0.006485965, + "call": -0.006927921, + "custom": -0.059661351, + "servic": -0.030051192, + "support": -0.029610159, + "number": -0.095319346, + "contact": -0.00818897, + "helplin": -0.027593594, + "status": -1.433053613, + "know": -0.088160224, + "return": -0.088160224, + "chang": -0.139278173, + "profil": -0.066707537, + "inform": -0.066707537, + "password": -0.064918637, + "phone": -0.009016867, + "address": -0.007086353, + "reset": -0.008451225, + "delet": -0.321581721, + "account": -0.321581721, + "common": -0.378452688, + "reason": -1.844274163, + "refund": -0.590106189, + "receiv": -0.200572416, + "track": -0.81316787, + "nonefeature": 2.6e-7 + } + }, + "delivery": { + "bias": -0.700778718, + "weights": { + "can": -0.610703945, + "i": -1.03035748, + "cancel": -0.006151776, + "my": 1.387134075, + "order": -0.409993261, + "whi": -0.006151776, + "is": -0.486358523, + "how": 1.584909439, + "do": -0.589591146, + "an": -0.003932309, + "did": -0.009784149, + "get": -0.009784149, + "ani": -0.01330458, + "what": -0.403264731, + "delay": -1.526154637, + "long": 3.484032393, + "doe": 3.484032393, + "deliveri": 2.239437342, + "take": 2.391818047, + "ship": 2.26478982, + "pleas": 2.536566257, + "tell": 2.536566257, + "me": 1.961959124, + "about": 2.536566257, + "goodby": -0.199656576, + "bye": -0.908763349, + "care": -0.809895456, + "see": -0.13074632, + "you": -0.790201604, + "later": -0.13074632, + "for": -0.515491605, + "now": -0.098867871, + "must": -0.117900781, + "go": -0.117900781, + "hi": -0.144212514, + "howdi": -0.136842534, + "greet": -0.129845858, + "anyon": -0.093649961, + "there": -0.160237893, + "good": -0.102606177, + "day": -0.315322876, + "which": -0.068892926, + "item": -0.153021023, + "have": -0.08219751, + "kind": -0.066587925, + "of": -0.087817602, + "are": -0.322417587, + "sell": -0.04598308, + "offer": -0.031896338, + "buy": -0.080433242, + "am": -0.089072481, + "look": -0.048008692, + "interest": -0.041063789, + "in": -0.041063789, + "want": -0.089900576, + "to": -0.886791527, + "a": -0.392226964, + "someth": -0.005892708, + "like": -0.005892708, + "this": -0.005892708, + "your": -0.25148651, + "most": -0.017540164, + "popular": -0.017540164, + "some": -0.021229673, + "best": -0.021229673, + "deal": -0.021229673, + "need": -0.059132416, + "more": -0.633740664, + "help": -0.633740664, + "talk": -0.003932309, + "agent": -0.003932309, + "call": -0.006976326, + "custom": -0.349959284, + "servic": -0.306066215, + "support": -0.043893099, + "number": -0.084470235, + "contact": -0.41857478, + "helplin": -0.04057714, + "becom": -0.238496244, + "seller": -0.357981175, + "status": -0.365382493, + "know": -0.055654794, + "return": -0.191542387, + "credit": -0.282318592, + "card": -0.282318592, + "pay": -0.122779578, + "chang": -0.333390713, + "profil": -0.333390713, + "inform": -0.333390713, + "delet": -0.350394726, + "account": -0.350394726, + "common": -0.36861515, + "reason": -0.36861515, + "track": -0.220217168, + "nonefeature": 2.6e-7 + } + }, + "greetings.bye": { + "bias": 1.482027045, + "weights": { + "can": -1.783964872, + "i": 0.753705263, + "my": -0.744887352, + "order": -0.772368312, + "is": -0.559961081, + "how": -1.109860539, + "do": -2.02209568, + "an": -0.051306263, + "payment": -0.192733005, + "was": -0.260007441, + "but": -0.192733005, + "did": -0.062434334, + "not": -0.369162798, + "ani": -0.13553302, + "confirm": -0.260007441, + "what": -2.281577587, + "complet": -0.192733005, + "no": -0.377738476, + "money": -0.301888317, + "delay": -1.183904767, + "long": -0.695258796, + "doe": -0.695258796, + "deliveri": -0.597039878, + "take": 1.442578197, + "ship": -0.419695586, + "me": -0.238853157, + "goodby": 10.377142906, + "bye": 6.57363224, + "care": 2.978529453, + "see": 4.61480093, + "you": 0.462690055, + "later": 5.856722832, + "for": 0.792383969, + "now": 3.595104933, + "must": 5.099290371, + "go": 5.099290371, + "hi": -1.5006634, + "howdi": -1.489623785, + "greet": -1.478035927, + "anyon": -0.505021989, + "there": -0.655818582, + "good": -1.093785644, + "day": -0.710998893, + "which": -0.407586753, + "item": -0.558383405, + "have": -1.785049558, + "kind": -0.150796711, + "of": -0.150796711, + "are": -1.016334295, + "sell": -0.32292971, + "offer": -0.242146477, + "buy": -0.630566776, + "am": -1.910566211, + "look": -1.325175047, + "interest": -0.266816765, + "in": -1.508746743, + "want": -0.588621855, + "to": -0.77574563, + "a": -0.382589281, + "someth": -0.140535131, + "like": -0.140535131, + "this": -0.140535131, + "need": -0.269211143, + "more": -0.508064151, + "help": -0.910015345, + "talk": -0.051306263, + "agent": -0.051306263, + "call": -0.054739516, + "custom": -0.470226586, + "servic": -0.252163321, + "support": -0.21806325, + "number": -1.130608201, + "contact": -0.410024941, + "helplin": -0.405885577, + "becom": -0.037336316, + "seller": -0.037336316, + "status": -0.313077956, + "know": -0.079985864, + "return": -0.12807247, + "credit": -0.840692043, + "card": -0.840692043, + "accept": -0.073207565, + "mastercard": -0.073207565, + "pay": -0.07375063, + "with": -0.07375063, + "cash": -0.582578063, + "onli": -0.508827567, + "chang": -0.13867408, + "password": -0.132956266, + "address": -0.068888821, + "reset": -0.063171007, + "delet": -0.40062204, + "account": -0.40062204, + "common": -0.255488276, + "reason": -1.117916465, + "refund": -0.564818978, + "avail": -0.077925235, + "when": -0.172386989, + "even": -0.012618554, + "though": -0.012618554, + "say": -0.172386989, + "receiv": -0.301888317, + "thank": -1.919605136, + "that": -0.04232106, + "thx": -1.385370255, + "thnks": -1.3671031, + "track": -0.705093861, + "im": -0.382787436, + "okay": -0.318574965, + "use": -0.038462117, + "voucher": -0.038462117, + "nonefeature": -1.322396755 + } + }, + "greetings.hello": { + "bias": 2.698667464, + "weights": { + "can": -0.60504806, + "i": -1.917989016, + "cancel": -0.008206869, + "my": -1.192340374, + "order": -1.559731841, + "whi": -0.184929654, + "is": -0.311327934, + "how": -4.098875523, + "do": -1.826153755, + "an": -0.111273386, + "payment": -0.387421727, + "was": -0.387693524, + "but": -0.415208668, + "not": -0.674776733, + "ani": -0.213093489, + "confirm": -0.415480494, + "what": -1.993245363, + "complet": -0.387421727, + "no": -0.696851373, + "money": -0.49778229, + "deduct": -0.027786948, + "delay": -1.586606622, + "deliveri": -1.23436892, + "take": -0.807368636, + "me": -0.462088704, + "goodby": -2.347725868, + "bye": -1.080813527, + "care": -0.771229148, + "see": -0.471309781, + "you": -1.701667905, + "later": -0.471309781, + "for": -1.260156155, + "now": -0.309584171, + "must": -0.360689968, + "go": -0.360689968, + "hi": 9.377306938, + "howdi": 9.369662285, + "greet": 9.281285286, + "anyon": 5.525262833, + "there": 4.409354687, + "good": 3.508930683, + "day": 5.511136532, + "which": -0.317819774, + "item": -1.433729053, + "have": -0.530913234, + "kind": -1.115909338, + "of": -1.127256632, + "are": -1.533455849, + "sell": -0.19289349, + "offer": -0.13181892, + "buy": -0.374793947, + "am": -0.77066499, + "look": -0.362793028, + "interest": -0.372954786, + "in": -0.372954786, + "want": -0.181585655, + "to": -0.673930883, + "a": -0.799342215, + "someth": -0.051081214, + "like": -0.051081214, + "this": -0.051081214, + "your": -3.729150772, + "some": -0.011347306, + "best": -0.011347306, + "deal": -0.011347306, + "new": -0.045455962, + "arriv": -0.045455962, + "need": -0.523759425, + "more": -0.985848129, + "help": -1.878553987, + "talk": -0.111273386, + "agent": -0.111273386, + "call": -0.082341753, + "custom": -1.003163695, + "servic": -0.525216877, + "support": -0.477946907, + "number": -1.86661768, + "contact": -0.251785755, + "helplin": -1.019777536, + "becom": -0.124325402, + "seller": -0.178371236, + "status": -0.901060641, + "return": -0.06684, + "credit": -0.212862298, + "card": -0.036139484, + "accept": -0.09910164, + "mastercard": -0.09910164, + "pay": -0.218224779, + "with": -0.218224779, + "cash": -0.35782811, + "onli": -0.139603361, + "delet": -0.836065888, + "account": -0.836065888, + "common": -0.173662707, + "reason": -0.525900602, + "refund": -1.22274375, + "avail": -0.26659593, + "the": -0.176722825, + "when": -0.198360458, + "it": -0.176722825, + "even": -0.287792295, + "though": -0.287792295, + "say": -0.021637654, + "receiv": -0.469995379, + "thank": -2.654542446, + "that": -0.830826819, + "lot": -0.439385504, + "thx": -2.57408762, + "thnks": -2.540444374, + "track": -1.17539835, + "im": -5.720004559, + "okay": -0.034917634, + "nonefeature": -2.407108068 + } + }, + "items": { + "bias": -0.437643852, + "weights": { + "can": -2.162075996, + "i": 1.371232748, + "my": -2.894840956, + "order": -1.935244799, + "is": -1.39223659, + "how": -3.298545837, + "do": 2.637309074, + "an": -3.243030787, + "payment": -3.296664, + "was": -0.630252481, + "process": -0.630252481, + "success": -0.630252481, + "but": -0.630252481, + "did": -0.630252481, + "not": -0.630252481, + "get": -0.630252481, + "ani": 3.877095461, + "confirm": -0.630252481, + "what": 6.363491058, + "should": -0.630252481, + "delay": -0.99723804, + "deliveri": -0.354783773, + "take": -1.84620595, + "me": -0.395490855, + "bye": -0.972704113, + "see": -1.664452672, + "you": 1.609744668, + "later": -0.09757179, + "for": 2.427061558, + "now": -0.972704113, + "must": -0.461389661, + "go": -0.461389661, + "anyon": -0.008284496, + "there": 1.086342096, + "good": -1.106301188, + "which": 1.632452965, + "item": 2.582840919, + "have": 4.572917461, + "kind": 1.094626427, + "of": 2.456726313, + "are": -0.647481382, + "sell": 2.399790764, + "offer": 2.372802734, + "buy": 7.345085621, + "am": 4.225178719, + "look": 4.849271297, + "interest": 4.395131588, + "in": 2.82825017, + "want": 1.64228189, + "to": -0.126443163, + "a": 2.669211149, + "someth": 0.006806381, + "like": 0.006806381, + "this": 0.006806381, + "your": 0.666430712, + "most": 1.970742226, + "popular": 1.970742226, + "some": 1.36209929, + "best": 1.36209929, + "deal": 1.36209929, + "new": 0.134184629, + "arriv": 0.134184629, + "need": -0.464882344, + "more": -0.860373139, + "help": -1.312635422, + "talk": -1.12804985, + "agent": -1.12804985, + "call": -0.926660955, + "custom": -1.446992874, + "servic": -1.167274237, + "support": -0.279718697, + "number": -1.17300272, + "contact": -0.750090003, + "helplin": -0.349029988, + "becom": -0.34284839, + "seller": -0.492054224, + "status": -1.790418744, + "know": -0.485426009, + "return": -0.485426009, + "credit": -1.84620595, + "card": -1.84620595, + "accept": -2.245224237, + "mastercard": -2.245224237, + "pay": -1.638321996, + "with": -0.395812005, + "cash": -1.388990402, + "onli": -0.993178427, + "method": -2.666410446, + "chang": -0.310148567, + "password": -0.259830445, + "address": -0.127694324, + "reset": -0.077376217, + "delet": -0.0866464, + "account": -0.0866464, + "common": -0.354783773, + "reason": -0.99723804, + "refund": -2.114980936, + "thank": -1.217184901, + "lot": -0.078959547, + "thx": -0.018297104, + "thnks": -0.017425302, + "im": -1.106301188, + "okay": -5.019218922, + "use": -0.772900522, + "voucher": -0.772900522, + "nonefeature": 2.6e-7 + } + }, + "more.contact": { + "bias": 0.106528098, + "weights": { + "can": -0.222838446, + "i": -0.244069114, + "my": -0.665188968, + "order": -0.129259527, + "whi": -0.008058723, + "is": -1.797547579, + "how": -0.202562511, + "do": -0.173062861, + "an": -0.105529279, + "payment": -0.089555241, + "was": -0.031498812, + "but": -0.020652827, + "not": -0.096712969, + "confirm": -0.031498812, + "what": -0.119320318, + "complet": -0.020652827, + "no": -0.080035776, + "money": -0.077808246, + "delay": -0.098963626, + "deliveri": -0.63247782, + "take": -0.113014616, + "pleas": -0.541281581, + "tell": -0.541281581, + "me": 2.815993547, + "about": -0.541281581, + "goodby": -0.109490126, + "bye": -0.028607074, + "care": -0.028607074, + "you": -0.284087241, + "for": -1.108118892, + "hi": -0.107590616, + "howdi": -0.106522925, + "greet": -0.105720267, + "good": -0.105393134, + "day": -0.046644624, + "are": -0.217707217, + "am": -0.010419126, + "want": -0.007608225, + "to": -0.26918152, + "a": -0.06797833, + "your": -0.096509814, + "need": 2.822984695, + "more": 6.180260181, + "help": 3.392209053, + "talk": -0.105529279, + "agent": -0.105529279, + "call": -0.086530082, + "custom": -0.288224876, + "servic": -0.185968488, + "support": -0.102256402, + "number": -1.293886662, + "contact": -1.137471557, + "helplin": -0.04897533, + "becom": -0.06797833, + "seller": -0.06797833, + "status": -0.166405946, + "know": -0.007608225, + "return": -0.048546392, + "credit": -0.092466265, + "card": -0.084407546, + "accept": -0.078062415, + "mastercard": -0.078062415, + "pay": -0.033982389, + "with": -0.03077906, + "cash": -0.094852023, + "onli": -0.064072967, + "method": -0.068902418, + "delet": -0.065881245, + "account": -0.065881245, + "common": -0.010007572, + "reason": -0.017774943, + "refund": -0.214273974, + "avail": -0.069024049, + "the": -0.008058723, + "when": -0.053685665, + "it": -0.008058723, + "even": -0.013755993, + "though": -0.013755993, + "say": -0.045626946, + "receiv": -0.077808246, + "thank": -0.216784522, + "that": -1.697707772, + "thx": -0.133782417, + "thnks": -0.129948378, + "track": -0.067995638, + "im": -0.086355917, + "okay": -0.010419126, + "nonefeature": -0.122527488 + } + }, + "more.help": { + "bias": 0.287461157, + "weights": { + "can": 2.551160336, + "i": -1.00156188, + "cancel": -1.146635413, + "my": -2.971086979, + "order": -1.639477253, + "is": -1.498378158, + "how": 0.422367692, + "do": -1.119657278, + "an": 0.971717596, + "payment": -0.238356426, + "was": -0.055171303, + "but": -0.055171303, + "not": -0.111386381, + "confirm": -0.055171303, + "what": -0.862001479, + "complet": -0.055171303, + "no": -0.055171303, + "money": -0.111386381, + "delay": -0.896346331, + "long": -0.170293182, + "doe": -0.170293182, + "deliveri": -0.45201841, + "take": -0.482170105, + "ship": -0.143822521, + "me": -0.533507586, + "goodby": -0.296472996, + "bye": -0.70013994, + "care": -0.003100271, + "you": -1.173532844, + "for": 1.090561867, + "now": -0.697039664, + "hi": -0.285657436, + "howdi": -0.28342551, + "greet": -0.281080604, + "good": -0.260243177, + "day": -0.344043344, + "item": -1.367340207, + "are": -0.812590301, + "buy": -0.876720309, + "am": -0.166600764, + "look": -0.166600764, + "want": -2.92526865, + "to": 2.803542614, + "a": 1.611396313, + "someth": -0.01458518, + "like": -0.01458518, + "this": -0.01458518, + "your": -0.527228415, + "need": -0.54384023, + "more": -1.077347517, + "help": 1.340989351, + "talk": 3.48569417, + "agent": 3.48569417, + "call": 4.634053707, + "custom": 3.71300292, + "servic": 2.110196352, + "support": 1.602806687, + "number": 6.755122185, + "contact": 3.034481525, + "helplin": 5.247686386, + "becom": 3.035251379, + "seller": 4.337525368, + "status": -2.22090745, + "know": -0.742487848, + "return": -1.810294628, + "credit": -0.308776647, + "card": -0.308776647, + "accept": -0.256953955, + "mastercard": -0.256953955, + "pay": -1.289173245, + "with": -0.828322113, + "cash": -0.994019091, + "onli": -0.165696964, + "method": -0.18318513, + "chang": -1.817986369, + "profil": -0.403455824, + "inform": -0.403455824, + "password": -0.057600718, + "phone": -1.356929898, + "common": -0.333809286, + "reason": -0.804607868, + "refund": -1.500329375, + "avail": -0.021602977, + "receiv": -0.111386381, + "thank": -0.675688624, + "that": -0.340472758, + "lot": -0.403249532, + "thx": -0.403669745, + "thnks": -0.392258108, + "track": -0.082229286, + "im": -0.260243177, + "use": -1.714365721, + "voucher": -1.714365721, + "nonefeature": -0.333226889 + } + }, + "order": { + "bias": -1.003031617, + "weights": { + "can": -0.782767832, + "i": -1.935054421, + "cancel": -0.797382116, + "my": 1.16455543, + "order": 1.60676074, + "whi": -1.771003485, + "is": 0.805561602, + "how": 0.689427972, + "do": -1.045280099, + "an": -0.019100323, + "payment": -0.358163625, + "was": -0.061909087, + "process": -0.060054161, + "success": -0.060054161, + "but": -0.185072556, + "did": -0.48292762, + "not": -0.785127699, + "get": -0.48292762, + "ani": -0.060054161, + "confirm": -0.186927497, + "what": 3.146201372, + "should": -0.060054161, + "no": -1.752007246, + "money": -0.125018373, + "deduct": -0.125018373, + "delay": -0.798744202, + "deliveri": -0.038689077, + "take": -0.143546417, + "pleas": -0.038381249, + "tell": -0.038381249, + "me": -0.038381249, + "about": -0.038381249, + "you": -0.902895808, + "for": -0.000323642, + "there": -0.269603729, + "day": -0.323646814, + "item": -0.350503236, + "kind": -0.269603729, + "of": -0.320687354, + "are": -0.932204306, + "sell": -0.198331267, + "offer": -0.189955279, + "want": -0.799599707, + "to": 1.192768097, + "a": -0.31881848, + "your": -0.734638929, + "most": -0.061799068, + "popular": -0.061799068, + "some": -0.051083583, + "best": -0.051083583, + "deal": -0.051083583, + "help": -0.025044078, + "custom": -0.266436428, + "servic": -0.266436428, + "number": -0.562881052, + "contact": -0.345103174, + "becom": -0.240151808, + "seller": -0.31881848, + "status": 6.770805836, + "know": 2.268250704, + "return": 4.845871925, + "credit": -0.741746545, + "card": -0.143546417, + "accept": -0.119454704, + "mastercard": -0.119454704, + "pay": -0.566202402, + "with": -0.273569733, + "cash": -0.347715974, + "onli": -0.074146189, + "method": -0.298109472, + "chang": -2.125850439, + "profil": -0.361521572, + "inform": -0.361521572, + "password": -1.203168511, + "phone": -0.562881052, + "address": -0.31700331, + "reset": -0.318724126, + "delet": -0.522327602, + "account": -0.522327602, + "common": -0.000182185, + "reason": -0.000323642, + "refund": -2.36930728, + "the": -0.598200142, + "when": -1.1253438, + "it": -0.598200142, + "even": -1.224863768, + "though": -1.224863768, + "say": -0.527143717, + "that": -0.025044078, + "track": -0.88577342, + "nonefeature": 2.6e-7 + } + }, + "payments": { + "bias": -0.228797696, + "weights": { + "can": -2.950469017, + "i": 2.422593594, + "cancel": -0.149429709, + "my": -1.745015264, + "order": -0.841024876, + "whi": -0.19050546, + "is": -0.773816466, + "how": -1.991226792, + "do": 3.143004656, + "an": -0.692349553, + "payment": 4.248058796, + "was": -0.948626637, + "process": -0.385240883, + "success": -0.385240883, + "but": -0.948626637, + "did": -0.385240883, + "not": -0.599747419, + "get": -0.385240883, + "ani": -1.085921049, + "confirm": -0.948626637, + "what": -0.598316669, + "should": -0.385240883, + "complet": -0.563385844, + "no": -0.594406366, + "money": -0.024001189, + "delay": -0.545467436, + "long": -0.137850702, + "doe": -0.137850702, + "deliveri": -0.369148433, + "take": 2.57158494, + "ship": -0.137850702, + "goodby": -0.111002497, + "bye": -1.160627842, + "care": -1.160627842, + "see": -0.393161327, + "you": -0.570030987, + "later": -0.37155214, + "for": -0.56138128, + "must": -1.037672162, + "go": -1.037672162, + "hi": -0.035371438, + "howdi": -0.033704516, + "greet": -0.032114424, + "there": -0.086112894, + "good": -1.45499742, + "day": -0.477643132, + "which": -0.711223364, + "item": -1.891830206, + "have": -1.433512807, + "kind": -0.086112894, + "of": -0.813399434, + "are": 0.323958099, + "sell": -1.904554009, + "offer": -1.755569816, + "buy": -0.363051414, + "am": -1.368916631, + "look": -0.212829143, + "interest": -0.367471993, + "in": -0.389081091, + "want": -0.633245587, + "to": -0.633245587, + "a": -0.536449611, + "someth": -0.006011911, + "like": -0.006011911, + "this": -0.006011911, + "your": 3.440179586, + "most": -0.551574588, + "popular": -0.551574588, + "some": -0.727286577, + "best": -0.727286577, + "deal": -0.727286577, + "new": -0.068054795, + "arriv": -0.068054795, + "help": -0.0947069, + "call": -0.176571086, + "custom": -0.176571086, + "servic": -0.176571086, + "number": -0.175979823, + "status": -0.221526012, + "credit": 3.679559231, + "card": 3.870063543, + "accept": 5.037496567, + "mastercard": 5.037496567, + "pay": 8.33078289, + "with": -0.467353135, + "cash": 5.946444035, + "onli": 6.413795948, + "method": 5.196681023, + "chang": -1.023448586, + "profil": -0.472414941, + "inform": -0.472414941, + "password": -0.367933244, + "phone": -0.175979823, + "address": -0.052774943, + "reset": -0.045654338, + "delet": -0.059039894, + "account": -0.059039894, + "common": -0.172233224, + "reason": -0.348552316, + "refund": -1.185259223, + "avail": -0.220094502, + "the": -0.19050546, + "when": -0.221526012, + "it": -0.19050546, + "say": -0.031020572, + "receiv": -0.024001189, + "thank": -0.961693525, + "that": -0.0947069, + "lot": -0.010961475, + "thx": -0.283866763, + "thnks": -0.272270262, + "track": -0.129636347, + "im": -1.45499742, + "okay": -0.788614452, + "use": -0.525488138, + "voucher": -0.525488138, + "nonefeature": -0.157367215 + } + }, + "personal": { + "bias": -2.371587977, + "weights": { + "can": -1.136502147, + "i": -2.992167234, + "cancel": -0.436704248, + "my": -1.327874184, + "order": -0.765577137, + "whi": -0.207515225, + "is": 2.656564236, + "how": 7.0323596, + "do": 0.069603108, + "an": -0.031143358, + "payment": -0.744351804, + "but": -0.037371621, + "not": -0.244886845, + "ani": -0.560658455, + "confirm": -0.037371621, + "what": -2.530439615, + "no": -0.153277785, + "money": -0.037371621, + "deduct": -0.037371621, + "delay": -0.298492849, + "long": -1.313526511, + "doe": -1.313526511, + "deliveri": -0.830887437, + "take": -1.341659904, + "ship": -0.687159836, + "see": -0.553340852, + "you": 3.404120445, + "later": -0.553340852, + "for": -0.215035141, + "anyon": -0.211387977, + "there": -0.325690329, + "good": -0.902838469, + "day": 3.161861181, + "which": -0.457234174, + "item": -1.108919978, + "have": -1.017892599, + "kind": -0.114302412, + "of": -0.607703209, + "are": 4.373958588, + "sell": -0.381310642, + "offer": -0.290834218, + "want": -0.148671612, + "to": -2.490938902, + "a": -0.914952278, + "your": 2.026182413, + "most": -0.506240129, + "popular": -0.506240129, + "some": -0.493400782, + "best": -0.493400782, + "deal": -0.493400782, + "new": -0.097725488, + "arriv": -0.097725488, + "help": -0.342907906, + "custom": -0.6883955, + "servic": -0.6883955, + "number": -0.020221855, + "contact": -0.880528331, + "becom": -0.56166935, + "seller": -0.753802299, + "status": -1.260861874, + "return": -0.90006882, + "credit": -0.235648572, + "card": -0.028133359, + "accept": -0.598447919, + "mastercard": -0.598447919, + "pay": -1.706993461, + "cash": -2.675266027, + "onli": -2.675266027, + "method": -0.744351804, + "chang": -0.499581784, + "profil": -0.405106723, + "inform": -0.405106723, + "password": -0.097838327, + "phone": -0.020221855, + "reset": -0.023585122, + "delet": -0.085795037, + "account": -0.085795037, + "common": -0.121063091, + "reason": -0.215035141, + "refund": -1.233407378, + "avail": -0.841471195, + "the": -0.207515225, + "when": -0.207515225, + "it": -0.207515225, + "even": -0.153277785, + "though": -0.153277785, + "thank": -1.028309345, + "that": -0.342907906, + "lot": -0.161150008, + "thx": -0.046663761, + "thnks": -0.0442825, + "track": -0.291501313, + "im": -0.294524997, + "nonefeature": -0.013221496 + } + }, + "profile.info": { + "bias": -0.133417878, + "weights": { + "can": -0.563632667, + "i": -0.569288969, + "cancel": -0.314670503, + "my": 4.948005199, + "order": -4.029072762, + "whi": -0.151632518, + "is": -0.273167908, + "how": 0.375885576, + "do": -0.12908034, + "an": -1.65895009, + "payment": -0.000245566, + "was": -0.000245566, + "but": -0.000245566, + "did": -0.681031883, + "not": -0.681031883, + "confirm": -0.000245566, + "complet": -0.000245566, + "no": -0.000245566, + "money": -0.681031883, + "delay": -0.545660317, + "long": -0.10831283, + "doe": -0.10831283, + "deliveri": -1.262806058, + "take": -0.10831283, + "ship": -0.097985856, + "pleas": -0.878604293, + "tell": -0.878604293, + "me": -0.878604293, + "about": -0.878604293, + "you": -0.159900054, + "for": -0.393095344, + "must": -0.020467769, + "go": -0.020467769, + "good": -0.014529927, + "item": -1.65895009, + "are": -0.056416519, + "buy": -0.886527002, + "am": -0.08655525, + "interest": -0.003431663, + "in": -0.003431663, + "want": 0.782398522, + "to": 0.367776811, + "a": -1.042017221, + "help": -0.09526442, + "custom": -0.513054371, + "servic": -0.341889262, + "support": -0.171165138, + "number": 0.814687788, + "contact": -0.311740637, + "helplin": -0.576205075, + "becom": -0.102880821, + "seller": -0.129219338, + "status": -1.899605393, + "know": -1.899605393, + "return": -1.899605393, + "chang": 4.715341568, + "profil": 1.86306572, + "inform": 1.86306572, + "password": 2.89017868, + "phone": 1.618544579, + "address": 2.351923227, + "reset": 4.008370876, + "delet": 3.712605, + "account": 3.712605, + "common": -0.221309572, + "reason": -0.393095344, + "refund": -3.275157213, + "receiv": -0.681031883, + "thank": -0.262397468, + "that": -0.09526442, + "lot": -0.026270946, + "thx": -0.134693563, + "thnks": -0.127963975, + "track": -2.779226542, + "im": -0.014529927, + "okay": -0.083123587, + "nonefeature": -0.005701863 + } + }, + "reason": { + "bias": 0.363332306, + "weights": { + "can": -0.237830117, + "i": -0.882256985, + "my": -2.899031162, + "order": -2.660119295, + "whi": -1.233136296, + "is": -1.443041205, + "how": -1.002487421, + "do": -0.110478155, + "an": -0.260359377, + "payment": -0.094403692, + "was": -0.108380795, + "but": -0.084411815, + "did": -1.326905012, + "not": -0.162350148, + "get": -1.326905012, + "ani": -0.000813878, + "confirm": -0.108380795, + "what": -0.037042294, + "complet": -0.084411815, + "no": -0.184281334, + "money": -0.051783398, + "delay": 7.946032524, + "long": -1.002487421, + "doe": -1.002487421, + "deliveri": 3.656396151, + "take": -1.002487421, + "pleas": -0.338314533, + "tell": -0.338314533, + "me": -0.338314533, + "about": -0.338314533, + "goodby": -0.297736287, + "bye": -0.850351095, + "see": -0.060173843, + "you": -0.223310918, + "later": -0.060173843, + "for": 0.948434591, + "now": -0.850351095, + "hi": -0.287174284, + "howdi": -0.281803876, + "greet": -0.276455998, + "good": -0.285025716, + "day": -0.035564646, + "item": -0.260359377, + "have": -0.000813878, + "of": -0.027050447, + "are": -0.154658437, + "am": -0.534625232, + "look": -0.502456367, + "want": -0.10980168, + "to": -0.10980168, + "a": -0.055892278, + "someth": -0.158683091, + "like": -0.158683091, + "this": -0.158683091, + "your": -0.037042294, + "some": -0.027050447, + "best": -0.027050447, + "deal": -0.027050447, + "new": -0.000274231, + "arriv": -0.000274231, + "help": -0.643726051, + "number": -0.551546395, + "contact": -0.551546395, + "status": -0.186467305, + "credit": -0.086597778, + "accept": -0.000373476, + "mastercard": -0.000373476, + "method": -0.009991848, + "common": -2.569491625, + "reason": 2.852787971, + "refund": -0.702709138, + "avail": -0.117616147, + "the": -0.086597778, + "when": -0.124634154, + "it": -0.086597778, + "even": -0.061833162, + "though": -0.061833162, + "say": -0.038036369, + "receiv": -0.051783398, + "thank": -0.418618649, + "that": -0.092179611, + "lot": -0.055892278, + "thx": -0.344294518, + "thnks": -0.340732872, + "track": -0.076223433, + "im": -0.249461025, + "okay": -0.032168876, + "nonefeature": -0.335134923 + } + }, + "refund.demanding": { + "bias": -1.357716805, + "weights": { + "can": 1.258229852, + "i": 0.253226697, + "cancel": -2.858938217, + "my": 1.126379848, + "order": 1.631664395, + "whi": -0.506008446, + "is": -1.385112524, + "how": -1.028966069, + "do": -0.460352391, + "an": 0.880323112, + "did": -1.617468596, + "not": -3.010694265, + "what": -1.381560087, + "no": -2.195140839, + "money": -2.504686117, + "you": -0.554710984, + "must": -0.030981287, + "go": -0.030981287, + "there": -0.714524806, + "good": -0.008165608, + "which": -0.460352391, + "item": 2.450227022, + "have": -0.460352391, + "kind": -0.714524806, + "of": -0.714524806, + "are": 1.564726472, + "buy": -0.277808398, + "am": -0.319347799, + "want": 0.962794125, + "to": -0.180677801, + "a": -0.90186882, + "your": -0.667035401, + "most": -0.667035401, + "popular": -0.667035401, + "talk": -1.143471599, + "agent": -1.143471599, + "call": -0.261716098, + "custom": -0.261716098, + "servic": -0.261716098, + "number": -0.117016502, + "status": -2.701149225, + "credit": -0.506008446, + "pay": -0.239562705, + "with": -0.239562705, + "cash": -0.239562705, + "chang": -0.547454298, + "profil": -0.064511016, + "inform": -0.064511016, + "password": -0.441843033, + "phone": -0.117016502, + "address": -0.19355832, + "reset": -0.269474506, + "delet": -0.339230269, + "account": -0.339230269, + "refund": 9.392977715, + "avail": 2.946285963, + "the": -0.506008446, + "when": -1.822044492, + "it": -0.506008446, + "even": -0.879104137, + "though": -0.879104137, + "say": -1.316035986, + "receiv": -2.504686117, + "thank": -0.225538954, + "thx": -0.12766391, + "thnks": -0.12098296, + "track": -2.869795322, + "im": -0.008165608, + "okay": -0.319347799, + "use": -0.624060452, + "voucher": -0.624060452, + "nonefeature": 2.6e-7 + } + }, + "refund.status": { + "bias": -0.577006254, + "weights": { + "can": -1.275535226, + "i": -0.964093506, + "cancel": -0.129577592, + "my": -0.212222055, + "order": -2.188269615, + "whi": 0.247978404, + "is": 0.171478286, + "how": -0.693302333, + "do": -0.429312944, + "an": -0.978632629, + "payment": -0.593195975, + "was": -0.60692966, + "process": -0.334855944, + "success": -0.334855944, + "but": -1.189339995, + "did": 1.233000994, + "not": 2.808225155, + "get": -0.334855944, + "ani": -0.334855944, + "confirm": -1.20307374, + "what": -0.583339632, + "should": -0.334855944, + "complet": -0.258340001, + "no": 2.854997158, + "money": 2.77925992, + "deduct": -0.596143544, + "deliveri": -0.011330971, + "take": -0.029870054, + "pleas": -0.011330971, + "tell": -0.011330971, + "me": -0.011330971, + "about": -0.011330971, + "goodby": -0.034834165, + "bye": -0.055330019, + "care": -0.029870054, + "see": -0.004233944, + "you": -0.218650654, + "later": -0.004233944, + "for": -0.025459966, + "now": -0.025459966, + "hi": -0.030127773, + "howdi": -0.028609822, + "greet": -0.027167447, + "anyon": -0.128689289, + "there": -0.128689289, + "good": -0.207964167, + "item": -0.978632629, + "are": -1.748355389, + "am": -0.135213017, + "want": -0.78634572, + "to": -1.084634781, + "a": -0.324194729, + "help": -0.413988143, + "custom": -0.033735305, + "servic": -0.015324207, + "support": -0.018411096, + "number": -0.047861107, + "helplin": -0.014125803, + "status": 2.753764629, + "know": -0.190355778, + "return": -0.488644898, + "credit": 0.377555996, + "refund": 4.081976891, + "avail": -1.748355389, + "the": 0.377555996, + "when": 1.883311272, + "it": 0.377555996, + "even": 1.607581496, + "though": 1.607581496, + "say": 1.505755663, + "receiv": 3.375402689, + "thank": -0.75262332, + "that": -0.413988143, + "lot": -0.296776801, + "thx": -0.179473341, + "thnks": -0.170546561, + "track": -0.80815053, + "im": -0.207964167, + "okay": -0.135213017, + "use": -0.027417935, + "voucher": -0.027417935, + "nonefeature": -0.103483677 + } + }, + "thanks": { + "bias": 2.3507558, + "weights": { + "can": -0.876109481, + "i": -1.820851088, + "cancel": -0.450405657, + "my": -1.703791261, + "order": -1.995606661, + "whi": -0.815269172, + "is": 1.532492638, + "how": -1.835814357, + "do": -1.964483976, + "payment": -0.24102062, + "was": -0.290304393, + "but": -0.615082979, + "not": -0.686882675, + "ani": -0.309037477, + "confirm": -0.664366722, + "what": -0.683308542, + "complet": -0.24102062, + "no": -0.847387552, + "money": -0.48224026, + "deduct": -0.37406233, + "delay": -1.102328658, + "long": -0.330891848, + "doe": -0.330891848, + "deliveri": -1.116755486, + "take": -0.879232347, + "ship": -0.142830655, + "pleas": -0.060637109, + "tell": -0.060637109, + "me": -1.504538417, + "about": -0.060637109, + "goodby": -2.176333427, + "bye": -0.909510374, + "care": -0.548340619, + "see": -1.470972657, + "you": 0.512296677, + "later": -1.470972657, + "for": -1.494753718, + "now": -0.361169845, + "must": -0.261287689, + "go": -0.261287689, + "hi": -2.13279891, + "howdi": -2.104790449, + "greet": -2.0764153, + "anyon": -1.89083159, + "there": -1.926821232, + "good": -1.25579834, + "day": -1.032160163, + "which": -0.312624961, + "item": -0.353116244, + "have": -0.621662199, + "kind": -0.035989624, + "of": -0.080920979, + "are": -1.151642561, + "sell": -0.326269537, + "offer": -0.271616459, + "buy": -0.039591487, + "am": -0.395922124, + "interest": -0.035752855, + "in": -0.035752855, + "want": -0.287532151, + "to": -0.456968278, + "a": -0.844384849, + "your": -0.944375396, + "most": -0.004501672, + "popular": -0.004501672, + "some": -0.044931352, + "best": -0.044931352, + "deal": -0.044931352, + "new": -0.048043016, + "arriv": -0.048043016, + "need": -1.537787437, + "more": -2.981688023, + "help": 2.238121033, + "custom": -0.371635705, + "servic": -0.17571038, + "support": -0.195925325, + "number": -1.877331972, + "contact": -1.129102349, + "helplin": -0.396878481, + "becom": -0.149150863, + "seller": -0.169436112, + "status": -0.761725903, + "credit": -0.15535903, + "accept": -0.433267832, + "mastercard": -0.433267832, + "cash": -0.846970856, + "onli": -0.846970856, + "delet": -0.279890448, + "account": -0.279890448, + "reason": -0.024766834, + "refund": -0.992837071, + "avail": -0.122932822, + "the": -0.15535903, + "when": -0.15535903, + "it": -0.15535903, + "even": -0.606366932, + "though": -0.606366932, + "receiv": -0.108178124, + "thank": 9.813996315, + "that": 6.328623772, + "lot": -0.214661554, + "thx": 9.699923515, + "thnks": 9.593873978, + "track": -0.912350118, + "im": -1.11858058, + "okay": -0.36016947, + "use": -0.420695871, + "voucher": -0.420695871, + "nonefeature": -2.21776557 + } + }, + "track": { + "bias": -0.746210544, + "weights": { + "can": 0.127043054, + "i": -0.210223466, + "cancel": -3.729145527, + "my": -0.267056346, + "order": 2.955234289, + "whi": -1.059894204, + "is": -1.340993404, + "how": 0.486462682, + "do": -1.319559813, + "an": -0.219547361, + "payment": -0.240521729, + "was": -0.761209607, + "process": -0.240521729, + "success": -0.240521729, + "but": -0.292782634, + "did": -0.839619815, + "not": -0.813470483, + "get": -0.839619815, + "ani": -0.240521729, + "confirm": -0.813470483, + "what": -0.469359845, + "should": -0.240521729, + "money": -0.052260935, + "deduct": -0.052260935, + "delay": -0.934813142, + "long": -0.135791063, + "doe": -0.135791063, + "deliveri": -0.091121987, + "take": -0.135791063, + "ship": -0.060032248, + "pleas": -0.015363174, + "tell": -0.015363174, + "me": -0.015363174, + "about": -0.015363174, + "you": -0.107412592, + "good": -0.354141235, + "item": -0.018813383, + "are": -0.107412592, + "buy": -0.419102162, + "am": -0.572401404, + "want": 0.475657165, + "to": 0.044201914, + "a": -1.050123096, + "talk": -0.14663662, + "agent": -0.14663662, + "custom": -0.270215929, + "servic": -0.270215929, + "number": -0.197758526, + "contact": -0.270215929, + "becom": -0.00327834, + "seller": -0.00327834, + "status": -0.681471765, + "know": -0.441309303, + "return": -0.45263347, + "pay": -0.007566913, + "with": -0.007566913, + "cash": -0.007566913, + "chang": -0.775216818, + "profil": -0.09945251, + "inform": -0.09945251, + "password": -0.505166352, + "phone": -0.197758526, + "address": -0.129004195, + "reset": -0.15616481, + "delet": -0.212166041, + "account": -0.212166041, + "refund": -1.741503835, + "track": 10.38419342, + "im": -0.354141235, + "okay": -0.572401404, + "use": -0.627742589, + "voucher": -0.627742589, + "nonefeature": -0.175189987 + } + }, + "user.response": { + "bias": 0.161040842, + "weights": { + "can": -0.839238346, + "i": 0.889569998, + "cancel": -0.622264147, + "my": -0.826192021, + "order": -0.820677221, + "whi": -0.085428931, + "is": -0.251974106, + "how": -1.207494617, + "do": 0.695894659, + "an": -0.106349945, + "payment": -0.275807738, + "was": -0.275807738, + "process": -0.198413044, + "success": -0.198413044, + "but": -0.275807738, + "did": -0.198413044, + "not": -0.198413044, + "get": -0.198413044, + "ani": -0.272808135, + "confirm": -0.275807738, + "what": -0.609856784, + "should": -0.198413044, + "complet": -0.077394709, + "no": -0.078305051, + "long": -0.203081936, + "doe": -0.203081936, + "deliveri": -0.162237525, + "take": -0.408013731, + "ship": -0.085581571, + "pleas": -0.044737179, + "tell": -0.044737179, + "me": -0.064674243, + "about": -0.044737179, + "goodby": -0.417665213, + "bye": -0.295586765, + "care": -0.171893522, + "see": -0.141765833, + "you": -1.128849268, + "later": -0.141765833, + "for": -2.28925848, + "now": -0.123693235, + "must": -0.585501671, + "go": -0.585501671, + "hi": -0.374192685, + "howdi": -0.36229369, + "greet": -0.351596713, + "anyon": -0.166545197, + "there": -0.166545197, + "good": 3.972707033, + "day": -3.952697277, + "which": -0.294237792, + "item": -0.361365378, + "have": -0.368632883, + "are": -0.036236335, + "sell": -0.232038692, + "offer": -0.179405034, + "buy": -0.154865816, + "am": 3.467407465, + "look": -2.165565252, + "interest": -2.214840889, + "in": -2.214840889, + "want": -0.221993446, + "to": -0.221993446, + "a": -0.426978946, + "someth": -0.164576471, + "like": -0.164576471, + "this": -0.164576471, + "need": -0.040609937, + "more": -0.060547002, + "help": -0.060547002, + "call": -0.030289605, + "custom": -0.088894665, + "servic": -0.057310417, + "support": -0.031584244, + "number": -0.103939384, + "helplin": -0.045334321, + "status": -0.000910351, + "credit": -0.033038247, + "card": -0.033038247, + "accept": -0.173968494, + "mastercard": -0.173968494, + "pay": -0.73269099, + "refund": -0.104274318, + "avail": -0.036236335, + "when": -0.000910351, + "say": -0.000910351, + "thank": -0.125409216, + "thx": -0.123471566, + "thnks": -0.121503092, + "im": 7.925405025, + "okay": 7.847812176, + "use": -0.272113293, + "voucher": -0.272113293, + "nonefeature": -0.465342522 + } + }, + "voucher": { + "bias": -0.490182323, + "weights": { + "can": 1.415617347, + "i": 0.452779979, + "cancel": -0.759827554, + "my": -0.603503823, + "order": -0.76677078, + "whi": -0.0003899, + "is": -0.723253727, + "how": -0.633529425, + "do": -0.238678455, + "an": -0.686769664, + "payment": -0.032580439, + "was": -0.032580439, + "but": -0.032580439, + "confirm": -0.032580439, + "what": -0.631405592, + "complet": -0.032580439, + "no": -0.032580439, + "long": -0.007381689, + "doe": -0.007381689, + "deliveri": -0.005234611, + "take": -0.039301526, + "ship": -0.002147079, + "goodby": -0.045438644, + "bye": -0.055411115, + "care": -0.031919837, + "see": -0.24018167, + "you": -0.24018167, + "later": -0.026646167, + "for": -0.136466935, + "now": -0.023491275, + "must": -0.196481481, + "go": -0.196481481, + "hi": -0.028869383, + "howdi": -0.027390188, + "greet": -0.025986236, + "anyon": -0.01539361, + "there": -0.026292838, + "good": -0.022018336, + "day": -0.022018336, + "item": -0.350977182, + "have": -0.213535503, + "kind": -0.010899226, + "of": -0.010899226, + "are": -0.010899226, + "buy": -0.978009224, + "am": -0.195317104, + "look": -0.11297565, + "interest": -0.082341447, + "in": -0.29587695, + "want": -0.571038306, + "to": -1.042594075, + "a": 2.629299879, + "talk": -0.133755445, + "agent": -0.133755445, + "call": -0.46018225, + "custom": -0.46018225, + "servic": -0.46018225, + "contact": -0.150736809, + "becom": -0.187063545, + "seller": -0.337800384, + "pay": -0.459420174, + "with": -0.459420174, + "cash": -0.459420174, + "chang": -0.049668964, + "profil": -0.049668964, + "inform": -0.049668964, + "refund": -0.340077966, + "thank": -0.707470238, + "lot": -0.707470238, + "track": -0.006943134, + "use": 4.24560976, + "voucher": 4.24560976, + "nonefeature": -0.08950004 + } + }, + "None": { + "bias": 0.792101142, + "weights": { + "can": -0.154368654, + "i": -0.519322157, + "cancel": -0.183626041, + "my": -0.439279854, + "order": -0.515051603, + "whi": -0.065024577, + "is": -0.349718392, + "how": -0.332969755, + "do": -0.224955082, + "an": -0.023454566, + "payment": -0.13554424, + "was": -0.164452672, + "process": -0.014886395, + "success": -0.014886395, + "but": -0.119875677, + "did": -0.04507589, + "not": -0.20145829, + "get": -0.04507589, + "ani": -0.060325667, + "confirm": -0.177471057, + "what": -0.198914826, + "should": -0.014886395, + "complet": -0.091970891, + "no": -0.159832403, + "money": -0.128976509, + "deduct": -0.013018386, + "delay": -0.383707851, + "long": -0.133312792, + "doe": -0.133312792, + "deliveri": -0.377474189, + "take": -0.298045665, + "ship": -0.061204772, + "pleas": -0.063173227, + "tell": -0.063173227, + "me": -0.167730987, + "about": -0.063173227, + "goodby": -0.736679673, + "bye": -0.334508628, + "care": -0.164732918, + "see": -0.198920205, + "you": -0.416279495, + "later": -0.198920205, + "for": -0.299671054, + "now": -0.16977571, + "must": -0.164796993, + "go": -0.164796993, + "hi": -0.72275728, + "howdi": -0.712830126, + "greet": -0.702781439, + "anyon": -0.197870195, + "there": -0.231533885, + "good": -0.508143544, + "day": -0.290650219, + "which": -0.032911122, + "item": -0.107973933, + "have": -0.07835038, + "kind": -0.033663709, + "of": -0.072893262, + "are": -0.303141952, + "sell": -0.023499187, + "offer": -0.017549954, + "buy": -0.02900826, + "am": -0.156261802, + "look": -0.014438643, + "interest": -0.081217878, + "in": -0.081217878, + "want": -0.02900826, + "to": -0.20438239, + "a": -0.112851657, + "your": -0.109315671, + "most": -0.041399125, + "popular": -0.041399125, + "some": -0.039229564, + "best": -0.039229564, + "deal": -0.039229564, + "new": -0.004591411, + "arriv": -0.004591411, + "need": -0.16515696, + "more": -0.269714683, + "help": -0.344300508, + "custom": -0.214380324, + "servic": -0.104482912, + "support": -0.109897427, + "number": -0.486834437, + "contact": -0.04906439, + "helplin": -0.27851215, + "becom": -0.049251787, + "seller": -0.076989375, + "status": -0.152553871, + "return": -0.084692314, + "accept": -0.066386238, + "mastercard": -0.066386238, + "pay": -0.030941335, + "with": -0.030941335, + "cash": -0.048725802, + "onli": -0.017784476, + "method": -0.028687021, + "delet": -0.166033581, + "account": -0.166033581, + "common": -0.001323052, + "reason": -0.107822418, + "refund": -0.326197773, + "avail": -0.142378137, + "when": -0.067861542, + "say": -0.067861542, + "receiv": -0.115958124, + "thank": -0.669407487, + "that": -0.066951193, + "lot": -0.006854028, + "thx": -0.654927731, + "thnks": -0.643414497, + "track": -0.210910201, + "im": -0.217493266, + "okay": -0.060605343, + "nonefeature": 10.995412827 + } + } + } ], - [ - -1.2913458347320557, - -1.1410175561904907, - -0.1504516303539276, - -0.4215856194496155, - -2.5707266330718994, - -0.003674748819321394, - 0.13974682986736298, - -0.6987276077270508, - -0.39745214581489563, - -1.0970606803894043, - -0.3989872634410858, - -0.5193899273872375, - -0.08799205720424652, - -0.08799205720424652, - -1.1870545148849487, - 1.5699361562728882, - 3.095979928970337, - -0.3248724043369293, - -0.08799205720424652, - -1.2793129682540894, - -0.37088605761528015, - -0.08799205720424652, - -0.23400215804576874, - 3.2401087284088135, - 3.209850788116455, - -0.7191513776779175, - -0.016282057389616966, - -0.31694793701171875, - -0.31694793701171875, - 2.076730966567993, - -0.18918496370315552, - -0.013376947492361069, - -0.013376947492361069, - -0.04278998076915741, - -0.004762548021972179, - -0.011217543855309486, - -0.011217543855309486, - -0.017532918602228165, - -0.011217543855309486, - -0.028222307562828064, - -0.0557534284889698, - -0.02368752472102642, - -0.014544656500220299, - -0.17036139965057373, - -0.014544656500220299, - -0.02665693685412407, - -0.02205268293619156, - -0.0021749138832092285, - -0.0021749138832092285, - -0.049194373190402985, - -0.023485533893108368, - -0.02252129279077053, - -0.021596623584628105, - -0.10593338310718536, - -0.11573706567287445, - -0.3629792332649231, - -0.0001258983975276351, - 0, - -1.1090625524520874, - 0, - -0.0036658381577581167, - -0.006373447831720114, - -2.0295188426971436, - 0, - 0, - 0, - -0.2419360876083374, - 0, - 0, - 0, - -0.722599983215332, - -1.3000450134277344, - -0.33351635932922363, - 0, - 0, - 0, - -0.00553872948512435, - -0.0023433116730302572, - -0.0023433116730302572, - -0.0008746905950829387, - -0.0008746905950829387, - -0.0008746905950829387, - 0, - 0, - -0.0038404290098696947, - -0.006467245519161224, - -0.29648056626319885, - 0, - 0, - 0, - -0.03231830894947052, - -0.012083077803254128, - -0.014325657859444618, - -0.050506096333265305, - 0, - -0.012288431636989117, - 0, - 0, - 3.2137577533721924, - -0.13101829588413239, - -0.6130020618438721, - 0.572363018989563, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.001087351469323039, - 4.734946250915527, - -2.0157506465911865, - 0.572363018989563, - 0.572363018989563, - 1.8228611946105957, - 1.8228611946105957, - 1.7091647386550903, - 3.9290292263031006, - -0.5727516412734985, - -0.26853373646736145, - -0.18373097479343414, - -0.12631776928901672, - -0.12113834172487259, - -0.5061092376708984, - -0.13984650373458862, - -0.0468229278922081, - -0.09297265112400055, - -0.09297265112400055, - -0.6886503062546867 - ], - [ - -0.5077318549156189, - -1.8987878561019897, - -0.35498031973838806, - -1.8763723373413086, - -2.0653798580169678, - -0.9951695799827576, - 1.7244404554367065, - -2.168811082839966, - -2.1415905952453613, - 0, - -0.23866921663284302, - -0.3075999617576599, - 0, - 0, - -0.6796745657920837, - 0, - -0.891762912273407, - 0, - -0.40114548802375793, - -0.7481383681297302, - -0.7970276474952698, - 0, - -0.23866921663284302, - -1.0001347064971924, - -0.5179142355918884, - -0.36288952827453613, - -1.3573659658432007, - -0.09471361339092255, - -0.09471361339092255, - -0.158460795879364, - -1.3037259578704834, - -0.24867312610149384, - -0.24867312610149384, - -0.9490104913711548, - -0.09194017201662064, - -0.10955855250358582, - -0.10955855250358582, - -1.783765196800232, - -0.10955855250358582, - -2.646313428878784, - -1.0704121589660645, - -0.6563863158226013, - -1.646806240081787, - 0.34065505862236023, - -1.646806240081787, - -1.748129963874817, - -0.3809452950954437, - -0.3920825123786926, - -0.3920825123786926, - -2.667757511138916, - -2.6342594623565674, - -2.626725673675537, - -2.6188108921051025, - -2.1725428104400635, - -2.231743335723877, - -1.5861741304397583, - -1.1299805641174316, - -0.2543991804122925, - -0.36038318276405334, - -0.6977182626724243, - -0.04082721844315529, - -0.0990259125828743, - -1.3207709789276123, - -0.32789477705955505, - -0.3065217435359955, - -0.04539057984948158, - -0.698974609375, - 0, - -0.035125356167554855, - -0.035125356167554855, - -0.22145676612854004, - -0.8647521138191223, - -1.1884366273880005, - 0, - 0, - 0, - -1.1018884181976318, - -0.02143898233771324, - -0.02143898233771324, - -0.04176821559667587, - -0.04176821559667587, - -0.04176821559667587, - -0.08753964304924011, - -0.08753964304924011, - -1.7882267236709595, - -3.4442903995513916, - 2.565450668334961, - 0, - 0, - 0, - -0.4173498749732971, - -0.18671193718910217, - -0.22115515172481537, - -2.18947696685791, - -1.294816017150879, - -0.47465768456459045, - -0.033020079135894775, - -0.033020079135894775, - -0.8756087422370911, - 0, - 0, - -0.1833024024963379, - -0.016651319339871407, - -0.5369012951850891, - -0.5369012951850891, - 0, - 0, - -0.9610477089881897, - -0.9610477089881897, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.3945724070072174, - -0.3945724070072174, - 0, - -0.03544429689645767, - -1.1593499183654785, - -0.18549960851669312, - -0.158460795879364, - -0.158460795879364, - -0.6906583905220032, - -0.6906583905220032, - 0, - -0.097763292491436, - 11.378676414489746, - 7.304425239562988, - -0.2728983163833618, - 11.550247192382812, - 11.530162811279297, - -0.7071039080619812, - -1.0914838314056396, - -0.2598259449005127, - -0.7921883463859558, - -0.7921883463859558, - 2.6784531290209324 - ], - [ - 0.380982369184494, - -0.3258844316005707, - -3.4917116165161133, - -0.27739769220352173, - 3.301130533218384, - -1.073818564414978, - -1.505022644996643, - 0.27774229645729065, - -1.3573098182678223, - -0.5270810127258301, - -0.24048076570034027, - -0.8727750778198242, - -0.24048076570034027, - -0.24048076570034027, - -0.3888031840324402, - -0.9459995627403259, - -1.035170555114746, - -0.9459995627403259, - -0.24048076570034027, - -0.9452674388885498, - -0.6623518466949463, - -0.24048076570034027, - 0, - 0, - -0.05696911737322807, - -0.05696911737322807, - -1.082931637763977, - -0.062191810458898544, - -0.062191810458898544, - 0, - -0.09859281778335571, - -0.08082381635904312, - -0.08082381635904312, - -0.08082381635904312, - -0.04772651940584183, - -0.053415942937135696, - -0.053415942937135696, - -0.053415942937135696, - -0.053415942937135696, - 0, - 0, - 0, - -0.005526010878384113, - -0.12121487408876419, - 0, - 0, - 0, - -0.007201648782938719, - -0.007201648782938719, - 0, - 0, - 0, - 0, - 0, - 0, - -0.7524583339691162, - 0, - 0, - 0, - -0.005526010878384113, - 0, - 0, - -0.11378240585327148, - 0, - 0, - -0.2490343451499939, - -0.8400887846946716, - 0, - 0, - -0.005526010878384113, - 0.6075114607810974, - -0.014191977679729462, - -0.8784146904945374, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.12548333406448364, - -0.12548333406448364, - -0.020310726016759872, - -0.2117539495229721, - -0.2117539495229721, - 0, - -0.20880654454231262, - -0.1780262440443039, - 0, - 0, - 0, - -0.756860613822937, - -0.37837353348731995, - -0.37837353348731995, - 0, - 0, - 0, - 0, - -0.026584208011627197, - -0.026584208011627197, - -0.026584208011627197, - 0, - 0, - -0.8632755875587463, - -0.09813888370990753, - -0.09813888370990753, - -0.5334068536758423, - -0.20880654454231262, - -0.141290083527565, - -0.18400461971759796, - -0.24847225844860077, - -0.24847225844860077, - 0, - 0, - -2.349536180496216, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 11.706945419311523, - -0.16522370278835297, - -0.22235870361328125, - -0.5889762043952942, - -0.5889762043952942, - -0.669537763085042 - ], - [ - -1.257106065750122, - 1.9722994565963745, - -0.7403843998908997, - -1.3834630250930786, - -0.97077476978302, - -0.05542337894439697, - -0.2643361985683441, - -1.355240821838379, - 0.2562214136123657, - -0.35549765825271606, - -0.2758129835128784, - -0.3064761757850647, - -0.10345030575990677, - -0.10345030575990677, - -0.2758129835128784, - -0.10345030575990677, - -0.16006582975387573, - -0.7584336996078491, - -0.2175266444683075, - -0.3064761757850647, - -0.5171506404876709, - -0.10345030575990677, - -0.09477495402097702, - -0.09477495402097702, - 0, - 0, - 0, - 0, - 0, - -0.5531495213508606, - -0.6761674284934998, - -0.16182109713554382, - -0.16182109713554382, - -0.36729753017425537, - -0.06748000532388687, - -0.008540223352611065, - -0.008540223352611065, - -0.026633432134985924, - -0.008540223352611065, - -0.3734872341156006, - -0.313742458820343, - -0.1641637533903122, - -0.1652340143918991, - -0.5971291661262512, - -0.1652340143918991, - -3.7121341228485107, - -0.09692328423261642, - -0.8412911891937256, - -0.8412911891937256, - -0.47956082224845886, - -0.3054158389568329, - -0.29459866881370544, - -0.2841568887233734, - -0.1532236486673355, - -0.19646738469600677, - 7.023808479309082, - -6.51149845123291, - -0.12688302993774414, - -0.3361934423446655, - -0.17926666140556335, - -0.026485782116651535, - -0.026485782116651535, - -0.0460079051554203, - -0.07561150193214417, - -0.05101285129785538, - -0.18610434234142303, - 5.773743629455566, - -3.558574914932251, - -3.6353445053100586, - -3.6353445053100586, - -0.19886760413646698, - -0.45692336559295654, - -0.6680894494056702, - -0.5418877601623535, - -0.5418877601623535, - -0.5418877601623535, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.014895623549818993, - -0.03276222199201584, - -0.03418026119470596, - 0, - 0, - -0.06170174479484558, - -0.09910255670547485, - -0.07873842865228653, - -0.013664050959050655, - -0.05212222784757614, - 0, - -0.014957047067582607, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.02163555845618248, - -0.02163555845618248, - -0.6897953748703003, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.14927265048027039, - -0.010437005199491978, - 0, - 0, - 0, - 0, - 0, - 0, - -0.017030984163284302, - -0.00009560387843521312, - 0, - -0.01634802483022213, - -0.015698639675974846, - 0, - 7.572530269622803, - 7.000411510467529, - -0.5314501523971558, - -0.5314501523971558, - -0.4734956401909367 - ], - [ - 1.0063420534133911, - -0.30277952551841736, - -0.6083040237426758, - -0.6710527539253235, - -0.6855297684669495, - -0.030787145718932152, - -0.7419338226318359, - 0.5955686569213867, - -0.34317153692245483, - -0.37119558453559875, - -0.09519360959529877, - -0.10691992193460464, - -0.025333672761917114, - -0.025333672761917114, - -0.09519360959529877, - -0.025333672761917114, - -0.038938913494348526, - -0.09744702279567719, - -0.025333672761917114, - -0.10691992193460464, - -0.3539024293422699, - -0.025333672761917114, - -0.05085968226194382, - -0.05085968226194382, - 0, - 0, - 0, - 0, - 0, - -0.05311308801174164, - -0.21816998720169067, - -0.2906172275543213, - -0.2906172275543213, - -0.3700330853462219, - -0.1103484034538269, - -0.00015175093722064048, - -0.00015175093722064048, - -0.0064401342533528805, - -0.00015175093722064048, - -0.05705874040722847, - -0.08894956856966019, - -0.030272455886006355, - -0.15075212717056274, - -0.1544485092163086, - -0.04713387414813042, - -0.12149176001548767, - -0.04507482424378395, - -0.09408246725797653, - -0.09408246725797653, - -0.09745470434427261, - -0.04398646950721741, - -0.04217682406306267, - -0.0404415987432003, - -0.029789570719003677, - -0.06194312870502472, - -0.03551335260272026, - -0.03551335260272026, - -0.0011829889845103025, - -0.021876968443393707, - -0.0818881243467331, - -0.019806738942861557, - -0.019806738942861557, - -0.019806738942861557, - 0, - 0, - -1.0367865562438965, - -0.12502995133399963, - -0.05542667210102081, - -0.04631301388144493, - -0.1500464528799057, - -0.8274975419044495, - 0.1079937145113945, - 2.482558488845825, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.007324249017983675, - -0.0165301151573658, - -0.0165301151573658, - -0.20215901732444763, - -0.20215901732444763, - -0.038466840982437134, - -0.26354047656059265, - -0.26284584403038025, - -0.00009888518980005756, - -0.00009888518980005756, - -0.9729569554328918, - 0, - -0.9938164949417114, - -1.7601009607315063, - -0.11758595705032349, - 0, - -0.11758595705032349, - 0, - 0, - 0, - 0, - -0.07950513064861298, - -0.07950513064861298, - -0.07950513064861298, - 0, - 0, - -0.05576881393790245, - -0.05576881393790245, - -0.05576881393790245, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -0.6361966729164124, - 0, - -0.6361966729164124, - 0, - 0, - 0, - 0, - 0, - 5.785028457641602, - 5.785028457641602, - -0.4704635307696706 - ] - ] + "trainOpts": { + "iterations": 20000, + "errorThresh": 0.00005, + "fixedError": false, + "deltaErrorThresh": 0.000001, + "learningRate": 0.7, + "momentum": 0.5, + "leakyReluAlpha": 0.08, + "maxDecimals": 9, + "log": false + } + } } } } } }, - "intentDomains": {}, "extraSentences": [ [ "en", @@ -5437,185 +4198,191 @@ ] ] }, - "ner": { - "settings": { - "tag": "ner", - "entityPreffix": "%", - "entitySuffix": "%" - }, - "rules": {} + "nerManager": { + "settings": {}, + "threshold": 0.8, + "builtins": [ + "Number", + "Ordinal", + "Percentage", + "Age", + "Currency", + "Dimension", + "Temperature", + "DateTime", + "PhoneNumber", + "IpAddress", + "Boolean", + "Email", + "Hashtag", + "URL" + ], + "namedEntities": {} }, - "nlgManager": { - "settings": { - "tag": "nlg-manager" - }, - "responses": { - "en": { - "cancel": [ - { - "answer": "Order can only be cancelled within 7 days of placement. Digital goods do not qualify for refund." - }, - { - "answer": "Visit my orders page to check status of specific orders." - } - ], - "confirm": [ - { - "answer": "knight sends you an Email & SMS upon confirmation of your order. If it requires manual confirmation, our team will contact you within 24 hours after order placement. Delay in confirmation SMS may occur due to network error, you may receive it with a delay." - }, - { - "answer": "It takes upto 24 hours for confirmation, please bear with us! Type Need more help for more assistance. " - } - ], - "delay": [ - { - "answer": "We are really sorry if there has been a delay in your order! If your order is within delivery period, We recommend you to please wait during this period since our Delivery Heroes make 3 attempts to deliver your order!" - }, - { - "answer": "If your order is past up to 3 days after delivery period, There may be logistic issues causing a delay in delivery. Please type 'Common reasons for delivery delay' to know more about this." - }, - { - "answer": "We appreciate if you could wait for your items as most orders are delivered successfully within this period." - }, - { - "answer": "If your order is past more than 3 days, Since there may be unexpected issues causing delivery delays, you can click on 'Need more Help' for further assistance." - } - ], - "delivery": [ - { - "answer": "Delivery takes 2-4 days. Please bear with us!" - }, - { - "answer": "Shipping takes 2-4 days. Please bear with us!" - } - ], - "greetings.bye": [ - { - "answer": "see you soon!" - }, - { - "answer": "Till next time" - }, - { - "answer": "bye bye" - }, - { - "answer": "have a great day" - }, - { - "answer": "See you later, thanks for visiting. Hope I was able to help!" - }, - { - "answer": "Have a nice day. Hope I was able to help!" - } - ], - "greetings.hello": [ - { - "answer": "Hey :-) My name is knight!" - }, - { - "answer": "Hello, thanks for visiting. My name is knight!" - }, - { - "answer": "Hi there, My name is knight!. What can I do for you?" - }, - { - "answer": "Hi there, My name is knight! How can I help?" - } - ], - "items": [ - { - "answer": "Search your preference in our flagship store's search bar to see all available products. " - } - ], - "more.contact": [ - { - "answer": "Absolutely! How can I help you today? Here are some options based on your inquiry:" - }, - { - "answer": "Sure, let me know what you need help with. Here are a few things I can assist you with:" - } - ], - "more.help": [ - { - "answer": "Contact us for further information here: Phone: +250 780 000 000. Timings are from 09:00 AM to 05:00 PM from Monday to Saturday." - }, - { - "answer": "For immediate assistance, you can contact our customer service team at +250 780 000 000. Our hours are from 09:00 AM to 05:00 PM, Monday to Saturday." - } - ], - "order": [ - { - "answer": "Please visit the My Orders page for a list of your confirmed orders." - } - ], - "payments": [ - { - "answer": "We accept VISA and Mastercard" - } - ], - "personal": [ - { - "answer": "I'm good, all's good, thanks. How about you?" - } - ], - "profile.info": [ - { - "answer": "You can easily add or change your account details by following the steps below: Step 1: Click on 'Account', Step 2: Click on 'Manage my account' from the icon, Step 3: You can change or edit your name, address, email address, mobile number, etc., Step 4: Fill in the required details, and click on Save. Note: You can also change your delivery address from the Checkout page before proceeding to pay." - } - ], - "reason": [ - { - "answer": "Reasons include Seller Sourcing Issues, Courier Issues, Cross Border shipment delay, Wrong Address or Phone Number, and Unavailability of Customer. " - } - ], - "refund.demanding": [ - { - "answer": "Refund can only be issued within 7 days of placement. Digital goods do not qualify for refund." - }, - { - "answer": "Visit my orders page to check for specific orders." - } - ], - "refund.status": [ - { - "answer": "Please be patient as refunds take upto 30 days to receive into bank. " - } - ], - "thanks": [ - { - "answer": "Happy to help!" - }, - { - "answer": "Any time!" - }, - { - "answer": "My pleasure" - } - ], - "track": [ - { - "answer": "Visit the order page, click on the specific order, select 'track my order', and check the status" - } - ], - "user.response": [ - { - "answer": "Great to hear you are doing good." - } - ], - "voucher": [ - { - "answer": "You can add a voucher by clicking on My Cart > Check Out > Enter Voucher Code > APPLY. " - } - ] - } + "slotManager": {}, + "responses": { + "en": { + "cancel": [ + { + "response": "Order can only be cancelled within 7 days of placement. Digital goods do not qualify for refund." + }, + { + "response": "Visit my orders page to check status of specific orders." + } + ], + "confirm": [ + { + "response": "knight sends you an Email & SMS upon confirmation of your order. If it requires manual confirmation, our team will contact you within 24 hours after order placement. Delay in confirmation SMS may occur due to network error, you may receive it with a delay." + }, + { + "response": "It takes upto 24 hours for confirmation, please bear with us! Type Need more help for more assistance. " + } + ], + "delay": [ + { + "response": "We are really sorry if there has been a delay in your order! If your order is within delivery period, We recommend you to please wait during this period since our Delivery Heroes make 3 attempts to deliver your order!" + }, + { + "response": "If your order is past up to 3 days after delivery period, There may be logistic issues causing a delay in delivery. Please type 'Common reasons for delivery delay' to know more about this." + }, + { + "response": "We appreciate if you could wait for your items as most orders are delivered successfully within this period." + }, + { + "response": "If your order is past more than 3 days, Since there may be unexpected issues causing delivery delays, you can click on 'Need more Help' for further assistance." + } + ], + "delivery": [ + { + "response": "Delivery takes 2-4 days. Please bear with us!" + }, + { + "response": "Shipping takes 2-4 days. Please bear with us!" + } + ], + "greetings.bye": [ + { + "response": "see you soon!" + }, + { + "response": "Till next time" + }, + { + "response": "bye bye" + }, + { + "response": "have a great day" + }, + { + "response": "See you later, thanks for visiting. Hope I was able to help!" + }, + { + "response": "Have a nice day. Hope I was able to help!" + } + ], + "greetings.hello": [ + { + "response": "Hey :-) My name is knight!" + }, + { + "response": "Hello, thanks for visiting. My name is knight!" + }, + { + "response": "Hi there, My name is knight!. What can I do for you?" + }, + { + "response": "Hi there, My name is knight! How can I help?" + } + ], + "items": [ + { + "response": "Search your preference in our flagship store's search bar to see all available products. " + } + ], + "more.contact": [ + { + "response": "Absolutely! How can I help you today? Here are some options based on your inquiry:" + }, + { + "response": "Sure, let me know what you need help with. Here are a few things I can assist you with:" + } + ], + "more.help": [ + { + "response": "Contact us for further information here: Phone: +250 780 000 000. Timings are from 09:00 AM to 05:00 PM from Monday to Saturday." + }, + { + "response": "For immediate assistance, you can contact our customer service team at +250 780 000 000. Our hours are from 09:00 AM to 05:00 PM, Monday to Saturday." + } + ], + "order": [ + { + "response": "Please visit the My Orders page for a list of your confirmed orders." + } + ], + "payments": [ + { + "response": "We accept VISA and Mastercard" + } + ], + "personal": [ + { + "response": "I'm good, all's good, thanks. How about you?" + } + ], + "profile.info": [ + { + "response": "You can easily add or change your account details by following the steps below: Step 1: Click on 'Account', Step 2: Click on 'Manage my account' from the icon, Step 3: You can change or edit your name, address, email address, mobile number, etc., Step 4: Fill in the required details, and click on Save. Note: You can also change your delivery address from the Checkout page before proceeding to pay." + } + ], + "reason": [ + { + "response": "Reasons include Seller Sourcing Issues, Courier Issues, Cross Border shipment delay, Wrong Address or Phone Number, and Unavailability of Customer. " + } + ], + "refund.demanding": [ + { + "response": "Refund can only be issued within 7 days of placement. Digital goods do not qualify for refund." + }, + { + "response": "Visit my orders page to check for specific orders." + } + ], + "refund.status": [ + { + "response": "Please be patient as refunds take upto 30 days to receive into bank. " + } + ], + "thanks": [ + { + "response": "Happy to help!" + }, + { + "response": "Any time!" + }, + { + "response": "My pleasure" + } + ], + "track": [ + { + "response": "Visit the order page, click on the specific order, select 'track my order', and check the status" + } + ], + "user.response": [ + { + "response": "Great to hear you are doing good." + } + ], + "voucher": [ + { + "response": "You can add a voucher by clicking on My Cart > Check Out > Enter Voucher Code > APPLY. " + } + ] } }, - "actionManager": { - "settings": { - "tag": "action-manager" - }, - "actions": {} - }, - "slotManager": {} + "actions": {}, + "utteranceDict": { + "?": "help" + } } \ No newline at end of file diff --git a/package.json b/package.json index 06430f2..ddbee4e 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,12 @@ "express-session": "^1.18.0", "express-winston": "^4.2.0", "highlight.js": "^11.9.0", - "joi": "^17.13.1", "jsend": "^1.1.0", "jsonwebtoken": "^9.0.2", "mailgen": "^2.0.28", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", - "node-nlp": "^4.27.0", + "node-nlp": "^3.10.2", "nodemailer": "^6.9.13", "nodemon": "^3.1.0", "passport": "^0.7.0", @@ -96,6 +95,7 @@ "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "jest-mock-extended": "^3.0.6", + "joi": "^17.13.1", "prettier": "^3.2.5", "supertest": "^7.0.0", "ts-jest": "^29.1.2", diff --git a/src/__test__/auth.test.ts b/src/__test__/auth.test.ts new file mode 100644 index 0000000..179a736 --- /dev/null +++ b/src/__test__/auth.test.ts @@ -0,0 +1,154 @@ +import request from 'supertest'; +import express, { Request, Response } from 'express'; +import { + userVerificationService, + userRegistrationService, + userLoginService, + userEnableTwoFactorAuth, + userDisableTwoFactorAuth, + userValidateOTP, + userResendOtpService, + logoutService, +} from '../services'; +import { userPasswordResetService } from '../services/userServices/userPasswordResetService'; +import { sendPasswordResetLinkService } from '../services/userServices/sendResetPasswordLinkService'; +import { activateUserService } from '../services/updateUserStatus/activateUserService'; +import { deactivateUserService } from '../services/updateUserStatus/deactivateUserService'; +import { userProfileUpdateServices } from '../services/userServices/userProfileUpdateServices'; +import { activateUser, disable2FA, disactivateUser, enable2FA, login, logout, resendOTP, sampleAPI, sendPasswordResetLink, userPasswordReset, userProfileUpdate, userRegistration, userVerification, verifyOTP } from '../controllers'; + +// Mock the services +jest.mock('../services', () => ({ + userVerificationService: jest.fn(), + userRegistrationService: jest.fn(), + userLoginService: jest.fn(), + userEnableTwoFactorAuth: jest.fn(), + userDisableTwoFactorAuth: jest.fn(), + userValidateOTP: jest.fn(), + userResendOtpService: jest.fn(), + logoutService: jest.fn(), +})); + +jest.mock('../services/userServices/userPasswordResetService', () => ({ + userPasswordResetService: jest.fn(), +})); + +jest.mock('../services/userServices/sendResetPasswordLinkService', () => ({ + sendPasswordResetLinkService: jest.fn(), +})); + +jest.mock('../services/updateUserStatus/activateUserService', () => ({ + activateUserService: jest.fn(), +})); + +jest.mock('../services/updateUserStatus/deactivateUserService', () => ({ + deactivateUserService: jest.fn(), +})); + +jest.mock('../services/userServices/userProfileUpdateServices', () => ({ + userProfileUpdateServices: jest.fn(), +})); + +const app = express(); +app.use(express.json()); + +app.post('/register', userRegistration); +app.post('/verify', userVerification); +app.post('/login', login); +app.post('/enable-2fa', enable2FA); +app.post('/disable-2fa', disable2FA); +app.post('/verify-otp', verifyOTP); +app.post('/resend-otp', resendOTP); +app.get('/sample', sampleAPI); +app.post('/reset-password', userPasswordReset); +app.post('/send-reset-link', sendPasswordResetLink); +app.post('/activate', activateUser); +app.post('/deactivate', disactivateUser); +app.post('/logout', logout); +app.put('/update-profile', userProfileUpdate); + +describe('User Controller', () => { + it('should call userRegistrationService on /register', async () => { + (userRegistrationService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(201).send()); + await request(app).post('/register').send({}); + expect(userRegistrationService).toHaveBeenCalled(); + }); + + it('should call userVerificationService on /verify', async () => { + (userVerificationService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/verify').send({}); + expect(userVerificationService).toHaveBeenCalled(); + }); + + it('should call userLoginService on /login', async () => { + (userLoginService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/login').send({}); + expect(userLoginService).toHaveBeenCalled(); + }); + + it('should call userEnableTwoFactorAuth on /enable-2fa', async () => { + (userEnableTwoFactorAuth as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/enable-2fa').send({}); + expect(userEnableTwoFactorAuth).toHaveBeenCalled(); + }); + + it('should call userDisableTwoFactorAuth on /disable-2fa', async () => { + (userDisableTwoFactorAuth as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/disable-2fa').send({}); + expect(userDisableTwoFactorAuth).toHaveBeenCalled(); + }); + + it('should call userValidateOTP on /verify-otp', async () => { + (userValidateOTP as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/verify-otp').send({}); + expect(userValidateOTP).toHaveBeenCalled(); + }); + + it('should call userResendOtpService on /resend-otp', async () => { + (userResendOtpService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/resend-otp').send({}); + expect(userResendOtpService).toHaveBeenCalled(); + }); + + it('should return 200 on /sample', async () => { + const response = await request(app).get('/sample'); + expect(response.status).toBe(200); + expect(response.body).toEqual({ message: 'Token is valid' }); + }); + + it('should call userPasswordResetService on /reset-password', async () => { + (userPasswordResetService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/reset-password').send({}); + expect(userPasswordResetService).toHaveBeenCalled(); + }); + + it('should call sendPasswordResetLinkService on /send-reset-link', async () => { + (sendPasswordResetLinkService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/send-reset-link').send({}); + expect(sendPasswordResetLinkService).toHaveBeenCalled(); + }); + + it('should call activateUserService on /activate', async () => { + (activateUserService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/activate').send({}); + expect(activateUserService).toHaveBeenCalled(); + }); + + it('should call deactivateUserService on /deactivate', async () => { + (deactivateUserService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/deactivate').send({}); + expect(deactivateUserService).toHaveBeenCalled(); + }); + + it('should call logoutService on /logout', async () => { + (logoutService as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).post('/logout').send({}); + expect(logoutService).toHaveBeenCalled(); + }); + + it('should call userProfileUpdateServices on /update-profile', async () => { + (userProfileUpdateServices as jest.Mock).mockImplementationOnce((req: Request, res: Response) => res.status(200).send()); + await request(app).put('/update-profile').send({}); + expect(userProfileUpdateServices).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/__test__/cart.test.ts b/src/__test__/cart.test.ts index ffe143f..40321f1 100644 --- a/src/__test__/cart.test.ts +++ b/src/__test__/cart.test.ts @@ -1,3 +1,4 @@ + import request from 'supertest'; import jwt from 'jsonwebtoken'; import { app, server } from '../index'; @@ -10,22 +11,49 @@ import { Category } from '../entities/Category'; import { Cart } from '../entities/Cart'; import { CartItem } from '../entities/CartItem'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { updateOrderService } from '../services/orderServices/updateOrderService'; const vendor1Id = uuid(); + const buyer1Id = uuid(); const buyer2Id = uuid(); const buyer3Id = uuid(); +const buyer4Id = uuid(); + const product1Id = uuid(); const product2Id = uuid(); + const catId = uuid(); const cart1Id = uuid(); const cartItemId = uuid(); + const sampleCartId = uuid(); const sampleCartItemId = uuid(); const samplecartItem3Id = uuid(); +const sampleAdminId = uuid(); + +let returnedCartId: string; +let returnedCartItemId: string; +let returnedCartItem2Id: string; const jwtSecretKey = process.env.JWT_SECRET || ''; +if (!process.env.TEST_USER_EMAIL || !process.env.TEST_USER_PASS) throw new Error('TEST_USER_PASS or TEST_USER_EMAIL not set in .env'); + +const sampleAdmin: UserInterface = { + id: sampleAdminId, + firstName: 'admin!', + lastName: 'user', + email: 'admin@gmail.com', + password: 'admin', + userType: 'Admin', + gender: 'Male', + phoneNumber: '10026386347', + photoUrl: 'https://example.com/photo.jpg', + role: 'ADMIN', +}; + + const getAccessToken = (id: string, email: string) => { return jwt.sign( { @@ -64,7 +92,7 @@ const sampleBuyer1: UserInterface = { const sampleBuyer2: UserInterface = { id: buyer2Id, - firstName: 'buyer1', + firstName: 'buyer2', lastName: 'user', email: 'elijahladdiedv@example.com', password: 'password', @@ -76,15 +104,27 @@ const sampleBuyer2: UserInterface = { }; const sampleBuyer3: UserInterface = { id: buyer3Id, - firstName: 'buyer1', + firstName: 'buyer3', lastName: 'user', - email: 'elhladdiedv@example.com', + email: 'buyer3@example.com', password: 'password', - userType: 'Admin', + userType: 'Buyer', gender: 'Male', - phoneNumber: '121163800', + phoneNumber: '1211ddf3800', photoUrl: 'https://example.com/photo.jpg', - role: 'ADMIN', + role: 'BUYER', +}; +const sampleBuyer4: UserInterface = { + id: buyer4Id, + firstName: 'buyer4', + lastName: 'user', + email: 'buyer4@example.com', + password: 'password', + userType: 'Buyer', + gender: 'Male', + phoneNumber: '1211ddsdff3800', + photoUrl: 'https://example.com/photo.jpg', + role: 'BUYER', }; const sampleCat = { @@ -98,7 +138,7 @@ const sampleProduct1 = { description: 'amazing product', images: ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'], newPrice: 200, - quantity: 10, + quantity: 20, vendor: sampleVendor1, categories: [sampleCat], }; @@ -152,7 +192,7 @@ const sampleCartItem3 = { total: 400, }; -const bodyTosend = { +const bodyToSend = { productId: product1Id, quantity: 2, }; @@ -167,6 +207,9 @@ beforeAll(async () => { await userRepository?.save({ ...sampleVendor1 }); await userRepository?.save({ ...sampleBuyer1 }); await userRepository?.save({ ...sampleBuyer2 }); + await userRepository?.save({ ...sampleBuyer3 }); + await userRepository?.save({ ...sampleBuyer4 }); + await userRepository?.save({ ...sampleAdmin }); const productRepository = connection?.getRepository(Product); await productRepository?.save({ ...sampleProduct1 }); @@ -189,137 +232,98 @@ afterAll(async () => { }); describe('Cart| Order management for guest/buyer', () => { - describe('Creating new product', () => { - it('should create new product', async () => { - const response = await request(app) - .post('/product') - .field('name', 'test product3') - .field('description', 'amazing product3') - .field('newPrice', 200) - .field('quantity', 10) - .field('expirationDate', '10-2-2023') - .field('categories', 'technology') - .field('categories', 'sample') - .attach('images', `${__dirname}/test-assets/photo1.png`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); - expect(response.status).toBe(201); - expect(response.body.data.product).toBeDefined; - }); - - it('return an error if the number of product images exceeds 6', async () => { - const response = await request(app) - .post(`/product/`) - .field('name', 'test-product-images') - .field('description', 'amazing product3') - .field('newPrice', 200) - .field('quantity', 10) - .field('expirationDate', '10-2-2023') - .field('categories', 'technology') - .field('categories', 'sample') - .attach('images', `${__dirname}/test-assets/photo1.png`) - .attach('images', `${__dirname}/test-assets/photo1.png`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Product cannot have more than 6 images'); - }); - - it('should not create new product it already exist', async () => { + describe('Adding product to cart on guest/buyer', () => { + it('should add product to cart as authenticated buyer', async () => { const response = await request(app) - .post('/product') - .field('name', 'test product3') - .field('description', 'amazing product3') - .field('newPrice', 200) - .field('quantity', 10) - .field('categories', sampleCat.name) - .attach('images', `${__dirname}/test-assets/photo1.png`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + .post(`/cart`) + .send(bodyToSend) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(409); + expect(response.status).toBe(201); + expect(response.body.data.message).toBe('cart updated successfully'); + expect(response.body.data.cart).toBeDefined; }); - it('should not create new product, if there are missing field data', async () => { + it('should add product to cart as authenticated buyer2', async () => { const response = await request(app) - .post('/product') - .field('description', 'amazing product3') - .field('newPrice', 200) - .field('quantity', 10) - .field('categories', sampleCat.name) - .attach('images', `${__dirname}/test-assets/photo1.png`) - .attach('images', `${__dirname}/test-assets/photo2.webp`) - .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + .post(`/cart`) + .send({ + productId: product2Id, + quantity: 200, + }) + .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - expect(response.status).toBe(400); + expect(response.status).toBe(201); + expect(response.body.data.message).toBe('cart updated successfully'); + expect(response.body.data.cart).toBeDefined; }); - it('should not create new product, images are not at least more than 1', async () => { + it('should add product to cart as authenticated buyer (buyer4)', async () => { const response = await request(app) - .post('/product') - .field('name', 'test-product-image') - .field('description', 'amazing product3') - .field('newPrice', 200) - .field('quantity', 10) - .field('categories', sampleCat.name) - .attach('images', `${__dirname}/test-assets/photo1.png`) - .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + .post(`/cart`) + .send({ + productId: product2Id, + quantity: 1, + }) + .set('Authorization', `Bearer ${getAccessToken(buyer4Id, sampleBuyer4.email)}`); - expect(response.status).toBe(400); + expect(response.status).toBe(201); + expect(response.body.data.message).toBe('cart updated successfully'); + expect(response.body.data.cart).toBeDefined; }); - }); - describe('Adding product to cart on guest/buyer', () => { - it('should get cart items of authenticated user', async () => { - const response = await request(app) - .get('/cart') - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + it('should add product to cart as guest', async () => { + const response = await request(app).post(`/cart`).send(bodyToSend); - expect(response.status).toBe(200); - expect(response.body.data.message).toBe('Cart retrieved successfully'); + expect(response.status).toBe(201); + expect(response.body.data.message).toBe('cart updated successfully'); expect(response.body.data.cart).toBeDefined; - }); - it('should get cart items of authenticated user', async () => { - const response = await request(app) - .get('/cart') - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); + returnedCartId = response.body.data.cart.id; - expect(response.status).toBe(200); - expect(response.body.data.message).toBe('Cart is empty'); - expect(response.body.data.cart).toBeDefined; + returnedCartItemId = response.body.data.cart.items[0].id; }); - it('should add product to cart as authenticated buyer', async () => { + it('should update quantity of product, if it is already in cart of guest', async () => { const response = await request(app) .post(`/cart`) - .send(bodyTosend) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + .set('Cookie', [`cartId=${returnedCartId}`]) + .send({ + productId: product1Id, + quantity: 3, + }); expect(response.status).toBe(201); expect(response.body.data.message).toBe('cart updated successfully'); expect(response.body.data.cart).toBeDefined; }); - it('should add product to cart as guest', async () => { - const response = await request(app).post(`/cart`).send(bodyTosend); + it('should add second product to cart of guest', async () => { + const response = await request(app) + .post(`/cart`) + .set('Cookie', [`cartId=${returnedCartId}`]) + .send({ + productId: product2Id, + quantity: 3, + }); + returnedCartItem2Id = response.body.data.cart.items[1].id; expect(response.status).toBe(201); expect(response.body.data.message).toBe('cart updated successfully'); expect(response.body.data.cart).toBeDefined; }); - it('should get cart items of guest user', async () => { - const response = await request(app).get('/cart'); + it('should return 400 for incorrect Id syntax (IDs not in uuid form), when add product to cart', async () => { + const response = await request(app) + .post(`/cart`) + .set('Cookie', [`cartId=dfgdsf`]) + .send({ + productId: product1Id, + quantity: 3, + }); - expect(response.status).toBe(200); - expect(response.body.data.cart).toBeDefined; + expect(response.status).toBe(400); }); it('should return 400 if you do not send proper request body', async () => { @@ -348,7 +352,7 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.message).toBe('Quantity must be greater than 0'); }); - it('should chnage quantity of product in cart if it is already there', async () => { + it('should change quantity of product in cart if it is already there', async () => { const response = await request(app) .post(`/cart`) .send({ productId: product1Id, quantity: 3 }) @@ -371,13 +375,33 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.data.cart).toBeDefined; }); - it('should get cart items of guest user', async () => { + it('should get Empty cart items of authenticated user', async () => { + const response = await request(app) + .get('/cart') + .set('Authorization', `Bearer ${getAccessToken(buyer3Id, sampleBuyer3.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Cart is empty'); + expect(response.body.data.cart).toBeDefined; + }); + + it('should get Empty cart items of guest user', async () => { const response = await request(app).get('/cart'); expect(response.status).toBe(200); expect(response.body.data.cart).toBeDefined; }); + it('should get cart items of guest user', async () => { + const response = await request(app) + .get('/cart') + .set('Cookie', [`cartId=${returnedCartId}`]); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Cart retrieved successfully'); + expect(response.body.data.cart).toBeDefined; + }); + it('should get cart items of guest user as empty with wrong cartId', async () => { const response = await request(app) .get('/cart') @@ -387,15 +411,26 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.data.message).toBe('Cart is empty'); expect(response.body.data.cart).toBeDefined; }); + + + it('should return 400 for incorrect Id syntax (IDs not in uuid form), when getting cart', async () => { + const response = await request(app) + .get(`/cart`) + .set('Cookie', [`cartId=dfgdsf`]); + + expect(response.status).toBe(400); + }); }); describe('Order management tests', () => { let orderId: any; + let order2Id: any; let productId: any; let feedbackId: any; let feedback2Id: any; + describe('Create order', () => { - it('should return 400 when user ID is not provided', async () => { + it('should create order for authenticated user', async () => { const response = await request(app) .post('/product/orders') .send({ @@ -409,47 +444,92 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.status).toBe(201); }); - it('should return orders for the buyer', async () => { + it('create order for another authenticated user', async () => { const response = await request(app) - .get('/product/client/orders') - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(200); - orderId = response.body.data.orders[0]?.id; - productId = response.body.data.orders[0]?.orderItems[0]?.product?.id; - }); - - - it('should get single order', async () => { - const response = await request(app) - .get(`/product/client/orders/${orderId}`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + .post('/product/orders') + .send({ + address: { + country: 'Test Country', + city: 'Test City', + street: 'Test Street', + }, + }) + .set('Authorization', `Bearer ${getAccessToken(buyer4Id, sampleBuyer4.email)}`); - expect(response.status).toBe(200); - expect(response.body.data.order).toBeDefined(); + expect(response.status).toBe(201); + order2Id = response.body.data.id; }); - it('should not return data for single order, if order doesn\'t exist', async () => { + it('should not create an order if user has an empty cart or his carts has already been checked out', async () => { const response = await request(app) - .get(`/product/client/orders/${uuid()}`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - - expect(response.status).toBe(404); - }); - - it('should not return data for single order, for an incorrect id syntax', async () => { - const response = await request(app) - .get(`/product/client/orders/incorrectId`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - + .post('/product/orders') + .send({ + address: { + country: 'Test Country', + city: 'Test City', + street: 'Test Street', + }, + }) + .set('Authorization', `Bearer ${getAccessToken(buyer3Id, sampleBuyer3.email)}`); expect(response.status).toBe(400); }); - it('should return 404 if the buyer has no orders', async () => { + it('should not create an order, if there are not enough product in stock', async () => { const response = await request(app) - .get('/product/client/orders') + .post('/product/orders') + .send({ + address: { + country: 'Test Country', + city: 'Test City', + street: 'Test Street', + }, + }) .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - expect(response.status).toBe(404); - expect(response.body.message).toBeUndefined; + expect(response.status).toBe(400); + }); + describe('Retriving Info about oreder Test', () => { + it('should return orders for the buyer', async () => { + const response = await request(app) + .get('/product/client/orders') + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + orderId = response.body.data.orders[0]?.id; + productId = response.body.data.orders[0]?.orderItems[0]?.product?.id; + }); + + + it('should get single order', async () => { + const response = await request(app) + .get(`/product/client/orders/${orderId}`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.order).toBeDefined(); + }); + + it('should not return data for single order, if order doesn\'t exist', async () => { + const response = await request(app) + .get(`/product/client/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(404); + }); + + it('should not return data for single order, for an incorrect id syntax', async () => { + const response = await request(app) + .get(`/product/client/orders/incorrectId`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(400); + }); + + it('should return 404 if the buyer has no orders', async () => { + const response = await request(app) + .get('/product/client/orders') + .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); + expect(response.status).toBe(404); + expect(response.body.message).toBeUndefined; + }); }); it('should return transaction history for the buyer', async () => { @@ -460,11 +540,11 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.message).toBe('Transaction history retrieved successfully'); }); - it('should return 400 when user ID is not provided', async () => { + it('should return 400 when user is not AUTHORIZED', async () => { const response = await request(app) .get('/product/orders/history') - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(200); + .set('Authorization', `Bearer ''`); + expect(response.status).toBe(403); }); }); @@ -476,6 +556,22 @@ describe('Cart| Order management for guest/buyer', () => { .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); expect(response.status).toBe(200); }); + + it('should return 404, if order doesn\'t exist', async () => { + const response = await request(app) + .put(`/product/client/orders/${uuid()}`) + .send({ orderStatus: 'completed' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(404); + }); + + it('should return 200 and make refund to buyer when order is cancelled or returned', async () => { + const response = await request(app) + .put(`/product/client/orders/${order2Id}`) + .send({ orderStatus: 'cancelled' }) + .set('Authorization', `Bearer ${getAccessToken(buyer4Id, sampleBuyer4.email)}`); + expect(response.status).toBe(200); + }); }); describe('Add feedback to the product with order', () => { it('should create new feedback to the ordered product', async () => { @@ -484,9 +580,11 @@ describe('Cart| Order management for guest/buyer', () => { .send({ orderId, comment: 'Well this product looks so fantastic' }) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); expect(response.status).toBe(201); - feedbackId = response.body.data.id + feedbackId = response.body.data.id; }); - it('should create new feedback to the ordered product', async () => { + + + it('should create second feedback to the ordered product', async () => { const response = await request(app) .post(`/feedback/${productId}/new`) .send({ orderId, comment: 'Murigalike this product looks so fantastic' }) @@ -508,14 +606,143 @@ describe('Cart| Order management for guest/buyer', () => { .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); expect(response.status).toBe(200); }); - it('should remove recorder feedback as admin ', async () => { - const response = await request(app) - .delete(`/feedback/admin/delete/${feedback2Id}`) - .send({ orderId, comment: 'Well this product looks so lovely' }) - .set('Authorization', `Bearer ${getAccessToken(buyer3Id, sampleBuyer3.email)}`); - expect(response.status).toBe(401); + }); + + describe('Feedback API', () => { + + describe('Add feedback to the product with order', () => { + it('should create new feedback for the ordered product', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId, comment: 'Well this product looks so fantastic' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(201); + feedbackId = response.body.data.id; + }); + + it('should create another feedback for the ordered product', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId, comment: 'Murigalike this product looks so fantastic' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(201); + feedback2Id = response.body.data.id; + }); + + it('should fail to create feedback with missing orderId', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ comment: 'Missing orderId' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(404); + }); + + it('should fail to create feedback with missing comment', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(500); + }); + + it('should fail to create feedback with invalid productId', async () => { + const response = await request(app) + .post(`/feedback/invalidProductId/new`) + .send({ orderId, comment: 'Invalid productId' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(500); + }); + }); + + describe('Update feedback', () => { + it('should update existing feedback successfully', async () => { + const response = await request(app) + .put(`/feedback/update/${feedbackId}`) + .send({ orderId, comment: 'Well this product looks so lovely' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + + it('should fail to update feedback with invalid feedbackId', async () => { + const response = await request(app) + .put(`/feedback/update/invalidFeedbackId`) + .send({ orderId, comment: 'Invalid feedbackId' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(500); + }); + + it('should fail to update feedback without authorization', async () => { + const response = await request(app) + .put(`/feedback/update/${feedbackId}`) + .send({ orderId, comment: 'Unauthorized update' }); + expect(response.status).toBe(401); + }); + }); + + describe('Delete feedback', () => { + it('should remove recorded feedback', async () => { + const response = await request(app) + .delete(`/feedback/delete/${feedbackId}`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + + it('should fail to delete feedback with invalid feedbackId', async () => { + const response = await request(app) + .delete(`/feedback/delete/invalidFeedbackId`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(500); + }); + }); + + describe('Edge Cases', () => { + it('should not allow creating feedback for a product not in the order', async () => { + const invalidOrderId = 999; // Assuming an invalid orderId + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId: invalidOrderId, comment: 'Invalid orderId' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(500); + }); + + it('should fail to update feedback with a comment that is too long', async () => { + const longComment = 'a'.repeat(1001); // Assuming max length is 1000 + const response = await request(app) + .put(`/feedback/update/${feedback2Id}`) + .send({ orderId, comment: longComment }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + }); + + describe('Delete feedback by Admin', () => { + it('should delete feedback, if user is authenticated as admin', async () => { + const response = await request(app) + .delete(`/feedback/admin/delete/${feedback2Id}`) + .set('Authorization', `Bearer ${getAccessToken(sampleAdminId, sampleAdmin.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Feedback successfully removed'); + }); + + it('should return 404, if feedback doesn\'t exist', async () => { + const response = await request(app) + .delete(`/feedback/admin/delete/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(sampleAdminId, sampleAdmin.email)}`); + + expect(response.status).toBe(404); + }); + + it('should return 500, for incorrect feedback id syntax (invalid uuid) doesn\'t exist', async () => { + const response = await request(app) + .delete(`/feedback/admin/delete/invalid-uuid`) + .set('Authorization', `Bearer ${getAccessToken(sampleAdminId, sampleAdmin.email)}`); + + expect(response.status).toBe(500); + }); }); }); + }); describe('Deleting product from cart', () => { @@ -556,39 +783,31 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.data.message).toBe('cart removed successfully'); }); - it('should add product to cart as authenticated buyer', async () => { - const response = await request(app) - .post(`/cart`) - .send(bodyTosend) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + it('should delete product in guest cart', async () => { + const response = await request(app).delete(`/cart/${returnedCartItemId}`); - expect(response.status).toBe(201); - expect(response.body.data.message).toBe('cart updated successfully'); - expect(response.body.data.cart).toBeDefined; + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Product removed from cart successfully'); }); - it('should add product to cart as authenticated buyer', async () => { - const response = await request(app) - .post(`/cart`) - .send({ productId: product2Id, quantity: 2 }) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + it('should delete guest cart if there are no products remaining there', async () => { + const response = await request(app).delete(`/cart/${returnedCartItem2Id}`); - expect(response.status).toBe(201); - expect(response.body.data.message).toBe('cart updated successfully'); - expect(response.body.data.cart).toBeDefined; + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('cart removed successfully'); }); - it('should return 404 if product does not exist in guest cart', async () => { + it('should return 404 if Cart item (product) does not exist in guest cart', async () => { const response = await request(app).delete(`/cart/${uuid()}`); expect(response.status).toBe(404); expect(response.body.message).toBe('Cart item not found'); }); - it('should return 404 if product does not exist in guest cart', async () => { - const response = await request(app).delete(`/cart/${samplecartItem3Id}`); + it('should return 400, for incorrect Cart item (product) id syntax (invalid uuid)', async () => { + const response = await request(app).delete(`/cart/invalid-uuid`); - expect(response.status).toBe(200); + expect(response.status).toBe(400); }); }); @@ -596,24 +815,13 @@ describe('Cart| Order management for guest/buyer', () => { it('should return 200 as authenticated buyer does not have a cart', async () => { const response = await request(app) .delete(`/cart`) - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); + .set('Authorization', `Bearer ${getAccessToken(buyer3Id, sampleBuyer3.email)}`); expect(response.status).toBe(200); expect(response.body.data.message).toBe('Cart is empty'); expect(response.body.data.cart).toBeDefined; }); - it('should add product to cart as authenticated buyer', async () => { - const response = await request(app) - .post(`/cart`) - .send(bodyTosend) - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - - expect(response.status).toBe(201); - expect(response.body.data.message).toBe('cart updated successfully'); - expect(response.body.data.cart).toBeDefined; - }); - it('should clear cart as authenticated buyer', async () => { const response = await request(app) .delete(`/cart`) @@ -632,17 +840,17 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.data.cart).toBeDefined; }); - it('should get cart items of guest user as empty with wrong cartId', async () => { + it('should clear cart items of guest user', async () => { const response = await request(app) - .get('/cart') - .set('Cookie', [`cartId=${uuid()}`]); + .delete('/cart') + .set('Cookie', [`cartId=${sampleCartId}`]); expect(response.status).toBe(200); - expect(response.body.data.message).toBe('Cart is empty'); + expect(response.body.data.message).toBe('Cart cleared successfully'); expect(response.body.data.cart).toBeDefined; }); - it('should delete cart items of guest user as empty with wrong cartId', async () => { + it('should return empty cart for guest user, if he/she doesn\'t have cart', async () => { const response = await request(app) .delete('/cart') .set('Cookie', [`cartId=${sampleCartId}`]); @@ -651,5 +859,12 @@ describe('Cart| Order management for guest/buyer', () => { expect(response.body.data.message).toBe('Cart is empty'); expect(response.body.data.cart).toBeDefined; }); + + it('should return 400, for incorrect cart id syntax (invalid uuid) for guest user', async () => { + const response = await request(app).delete(`/cart`) + .set('Cookie', [`cartId=invalid-uuid`]); + + expect(response.status).toBe(400); + }); }); -}); +}); \ No newline at end of file diff --git a/src/__test__/coupon.test.ts b/src/__test__/coupon.test.ts index 269e95e..942942f 100644 --- a/src/__test__/coupon.test.ts +++ b/src/__test__/coupon.test.ts @@ -18,6 +18,7 @@ const buyer1Id = uuid(); const buyer2Id = uuid(); const product1Id = uuid(); const product2Id = uuid(); +const vendor2Id = uuid(); const couponCode = 'DISCOUNT20'; const couponCode1 = 'DISCOUNT10'; const couponCode2 = 'DISCOUNT99'; @@ -39,6 +40,20 @@ const getAccessToken = (id: string, email: string) => { ); }; +const sampleVendor2: UserInterface = { + id: vendor2Id, + firstName: 'Vendor', + lastName: 'User', + email: 'secondendor@example.com', + password: 'password123', + userType: 'Vendor', + gender: 'Male', + verified: true, + phoneNumber: '98000867890', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; + const sampleVendor1: UserInterface = { id: vendor1Id, firstName: 'Vendor', @@ -180,6 +195,7 @@ beforeAll(async () => { await userRepository?.save(sampleVendor1); await userRepository?.save(sampleBuyer1); await userRepository?.save(buyerNoCart); + await userRepository?.save(sampleVendor2); const productRepository = connection?.getRepository(Product); await productRepository?.save(sampleProduct1); @@ -224,7 +240,7 @@ describe('Coupon Management System', () => { expect(response.status).toBe(201); expect(response.body.status).toBe('success'); - }, 10000); + }); it('should return 400 for invalid coupon data', async () => { const response = await request(app) @@ -240,7 +256,55 @@ describe('Coupon Management System', () => { .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); expect(response.status).toBe(400); - }, 10000); + }); + + it('should return 403 if product not found', async () => { + const response = await request(app) + .post(`/coupons/vendor/${vendor1Id}/`) + .send({ + code: 'NEWCOUPON10', + discountRate: 10, + expirationDate: '2025-12-31', + maxUsageLimit: 50, + discountType: 'PERCENTAGE', + product: uuid(), + }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(403); + }) + + it('should return 402 if coupon already exist', async () => { + const response = await request(app) + .post(`/coupons/vendor/${vendor1Id}/`) + .send({ + code: couponCode1, + discountRate: 10, + expirationDate: '2025-12-31', + maxUsageLimit: 50, + discountType: 'PERCENTAGE', + product: product1Id, + }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(402); + }) + + it('should return 500 if there is server error', async () => { + const response = await request(app) + .post(`/coupons/vendor/***** -- + ---/`) + .send({ + code: 'NEWCOUPON', + discountRate: 10, + expirationDate: '2025-12-31', + maxUsageLimit: 50, + discountType: 'PERCENTAGE', + product: product1Id, + }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(500); + }) }); describe('Get All Coupons', () => { @@ -252,7 +316,7 @@ describe('Coupon Management System', () => { expect(response.status).toBe(200); expect(response.body.status).toBe('success'); expect(response.body.data).toBeInstanceOf(Object); - }, 10000); + }); it('should return 404 if no coupons found', async () => { const newVendorId = uuid(); @@ -261,7 +325,44 @@ describe('Coupon Management System', () => { .set('Authorization', `Bearer ${getAccessToken(newVendorId, 'newvendor@example.com')}`); expect(response.status).toBe(401); - }, 10000); + }); + }); + + describe('Vendor access all Coupon', () => { + it('should return all coupons', async () => { + const response = await request(app) + .get(`/coupons/vendor/${vendor1Id}/access-coupons`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + expect(response.body.status).toBe('success'); + }); + + it('should return 404 for invalid vendor id', async () => { + const invalidVendorId = uuid(); + const response = await request(app) + .get(`/coupons/vendor/${invalidVendorId}/access-coupons`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(404); + expect(response.body.message).toBe('User not found'); + }); + + it('should return 404 if no coupon found for VENDOR', async () => { + const response = await request(app) + .get(`/coupons/vendor/${vendor2Id}/access-coupons`) + .set('Authorization', `Bearer ${getAccessToken(vendor2Id, sampleVendor2.email)}`); + + expect(response.status).toBe(404); + }) + + it('should return 500 server error', async () => { + const response = await request(app) + .get(`/coupons/vendor/uihoji 090j hh =/access-coupons`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(500); + }) }); describe('Read Coupon', () => { @@ -271,7 +372,7 @@ describe('Coupon Management System', () => { .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); expect(response.status).toBe(200); - }, 10000); + }); it('should return 404 for invalid coupon code', async () => { const response = await request(app) @@ -281,7 +382,7 @@ describe('Coupon Management System', () => { expect(response.status).toBe(404); expect(response.body.status).toBe('error'); expect(response.body.message).toBe('Invalid coupon'); - }, 10000); + }); }); describe('Update Coupon', () => { @@ -295,7 +396,16 @@ describe('Coupon Management System', () => { expect(response.status).toBe(200); expect(response.body.status).toBe('success'); - }, 10000); + }); + + it('should validate coupon update input', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode1}`) + .send() + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(400); + }) it('should return 404 for updating a non-existent coupon', async () => { const response = await request(app) @@ -306,8 +416,79 @@ describe('Coupon Management System', () => { .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); expect(response.status).toBe(404); - expect(response.body.message).toBe('Coupon not found'); - }, 10000); + }); + + it('should return 200 for updating a discount of coupon', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ discountRate: 25 }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + }); + + it('should return 200 for updating a expirationDate of coupon', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ expirationDate: '2025-12-31' }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + }); + + it('should return 200 for updating a maxUsageLimit of coupon', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ maxUsageLimit: 40 }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + }); + + it('should return 200 for updating a discountType of coupon', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ discountType: 'MONEY' }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + }); + + it('should return 200 for updating a product of coupon', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ product: product1Id }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + }); + + it('should return 404, if product doesn\'t exist', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ product: uuid() }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(404); + }); + + it('should return 404 for coupon not found', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/===__8899jjhh`) + .send({ product: uuid() }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(404); + }); + + it('should return 500, incorrect product ids (invalid uuid)', async () => { + const response = await request(app) + .put(`/coupons/vendor/${vendor1Id}/update-coupon/${couponCode}`) + .send({ product: 'invalid-uuid' }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(500); + }); }); describe('Delete Coupon', () => { @@ -321,7 +502,7 @@ describe('Coupon Management System', () => { expect(response.status).toBe(200); expect(response.body.status).toBe('success'); - }, 10000); + }); it('should return 404 for deleting a non-existent coupon', async () => { const response = await request(app) @@ -334,7 +515,7 @@ describe('Coupon Management System', () => { expect(response.status).toBe(404); expect(response.body.status).toBe('error'); expect(response.body.message).toBe('Invalid coupon'); - }, 10000); + }); }); }); @@ -408,7 +589,7 @@ describe('Buyer Coupon Application', () => { `Coupon Code successfully activated discount on product: ${sampleProduct1.name}` ); }); - it('Should give discont when discount-type is money', async () => { + it('Should give discount when discount-type is money', async () => { const response = await request(app) .post(`/coupons/apply`) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) @@ -422,4 +603,4 @@ describe('Buyer Coupon Application', () => { ); }); }); -}); +}); \ No newline at end of file diff --git a/src/__test__/getProduct.test.ts b/src/__test__/getProduct.test.ts index 96201dd..94a1b0d 100644 --- a/src/__test__/getProduct.test.ts +++ b/src/__test__/getProduct.test.ts @@ -50,6 +50,7 @@ const sampleBuyer1: UserInterface = { phoneNumber: '000380996348', photoUrl: 'https://example.com/photo.jpg', role: 'BUYER', + }; const sampleCat = { @@ -67,7 +68,12 @@ const sampleProduct1 = { vendor: sampleVendor1, categories: [sampleCat], }; -let cardID : string; +const bodyTosend = { + productId: product1Id, + quantity: 2, +}; + +let cardID: string; beforeAll(async () => { const connection = await dbConnection(); @@ -75,7 +81,7 @@ beforeAll(async () => { await categoryRepository?.save({ ...sampleCat }); const userRepository = connection?.getRepository(User); - await userRepository?.save({ ...sampleVendor1}); + await userRepository?.save({ ...sampleVendor1 }); await userRepository?.save({ ...sampleBuyer1 }); const productRepository = connection?.getRepository(Product); @@ -136,23 +142,92 @@ describe('Get single product', () => { expect(response.body.message).toBe('Product not found'); }, 10000); }); -describe('Cart Order and payment functionalities', () => { - it('should create a cart for a product', async () => { - const productId = product1Id; - const quantity = 8; - - const token = getAccessToken(BuyerID, sampleBuyer1.email); +describe('POST /confirm-payment', () => { + it('should add product to cart as authenticated buyer', async () => { const response = await request(app) - .post('/cart') - .set('Authorization', `Bearer ${token}`) - .send({ productId, quantity }); + .post(`/cart`) + .send(bodyTosend) + .set('Authorization', `Bearer ${getAccessToken(BuyerID, sampleBuyer1.email)}`); + + expect(response.status).toBe(201); + expect(response.body.data.message).toBe('cart updated successfully'); + expect(response.body.data.cart).toBeDefined; - - expect(response.status).toBe(201); - expect(response.body.data.cart).toBeDefined(); cardID = JSON.stringify(response.body.data.cart.id) }); + it('should create an order successfully', async () => { + const address = { + country: 'Test Country', + city: 'Test City', + street: 'Test Street', + }; + + + const response = await request(app) + .post('/product/orders') + .set('Authorization', `Bearer ${getAccessToken(BuyerID, sampleBuyer1.email)}`) + .send({ address }); + + console.log(response.body.message) + expect(response.status).toBe(201); + expect(response.body.message).toBe('Order created successfully'); + expect(response.body.data).toBeDefined(); + + }); + it('should confirm payment successfully', async () => { + const token = 'your_valid_access_token_here'; + + + const response = await request(app) + .post(`/product/payment/${cardID}`) + .set('Authorization', `Bearer ${getAccessToken(BuyerID, sampleBuyer1.email)}`) + .send({ payment_method: "pm_card_visa" }); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Payment successful!'); + }); + + it('should handle cart not found', async () => { + + const response = await request(app) + .post(`/product/payment/wkowkokfowkf`) + .set('Authorization', `Bearer ${getAccessToken(BuyerID, sampleBuyer1.email)}`) + .send({ payment_method: "pm_card_visa" }); + + expect(response.status).toBe(200); + + }); } -) \ No newline at end of file +) +describe('GET / product search', () => { + + it('should return a 400 error if no name is provided', async () => { + const response = await request(app) + .get(`/product/search/`) + .query({ name: '' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Please provide a search term'); + }, 10000); + + it('should return products if name is provided', async () => { + const response = await request(app) + .get('/product/search') + .query({ name: 'test product3' }); + + expect(response.status).toBe(200); + expect(response.body.data).toBeDefined(); + expect(response.body.pagination).toBeDefined(); + }); + + it('should return a 404 error if no products are found', async () => { + const response = await request(app) + .get('/product/search') + .query({ name: 'nonexistentproduct' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('No products found'); + }); +}) \ No newline at end of file diff --git a/src/__test__/index.test.ts b/src/__test__/index.test.ts new file mode 100644 index 0000000..dfa39c4 --- /dev/null +++ b/src/__test__/index.test.ts @@ -0,0 +1,107 @@ +// index.test.ts + +import request from 'supertest'; +import { app, server } from '../index'; +import { dbConnection } from '../startups/dbConnection'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; + +beforeAll(async () => { + await dbConnection(); +}); + +afterAll(async () => { + await cleanDatabase(); + server.close(); +}); + +describe('Express App', () => { + it('should have JSON parsing enabled', async () => { + const response = await request(app) + .get('/test') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(200); + }); + + it('Should respond to posting route', async () => { + const response = await request(app) + .post('/test/posting') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(200); + }); + + it('should respond to a valid route', async () => { + const response = await request(app) + .get('/test') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Route works!'); + }); + + it('should not respond to invalid route', async () => { + const response = await request(app) + .get('/testing/mon') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(404); + }); + + it('should respond to an invalid route with an appropriate message', async () => { + const response = await request(app) + .get('/mon') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(404); + }); +}); + +describe('Application JSON', () =>{ +it('Should respond to json', async () =>{ +const data ={ + name: 'John', + age: 20, + gender:'male' +}; +const response = await request(app) +.post('/test/posting') +.set('Content-Type', 'application/json') +.send(data); + +expect(response.statusCode).toBe(200); +}); +}); + +describe('APIs protection', () => { + it('should respond with a 401 status for unauthorized request', async () => { + const response = await request(app) + .get('/test/secure') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(401); + }); + + it('should respond with a 500 status for server errors', async () => { + const response = await request(app) + .get('/test/error') + .set('Content-Type', 'application/json'); + + expect(response.status).toBe(500); + }); + + it('should respond with correct data', async () => { + const data = { + name: 'John', + age: 20, + gender: 'male' + }; + const response = await request(app) + .post('/test/posting') + .set('Content-Type', 'application/json') + .send(data); + + expect(response.status).toBe(200); + expect(response.body.data).toBeDefined; + }); +}); \ No newline at end of file diff --git a/src/__test__/index.utils.test.ts b/src/__test__/index.utils.test.ts new file mode 100644 index 0000000..60c7ded --- /dev/null +++ b/src/__test__/index.utils.test.ts @@ -0,0 +1,35 @@ +import { server } from '..'; +import { formatMoney, formatDate } from '../utils/index'; + +describe('Utility Functions', () => { + describe('formatMoney', () => { + it('should format a number as currency with default currency RWF', () => { + expect(formatMoney(1234.56)).toBeDefined(); + }); + + it('should format a number as currency with specified currency', () => { + expect(formatMoney(1234.56, 'USD')).toBe('$1,234.56'); + expect(formatMoney(1234.56, 'EUR')).toBe('€1,234.56'); + }); + + it('should format a number with no cents if amount is a whole number', () => { + expect(formatMoney(1234)).toBeDefined(); + }); + }); + + describe('formatDate', () => { + it('should format a date string into a more readable format', () => { + const date = new Date('2024-05-28'); + expect(formatDate(date)).toBe('May 28, 2024'); + }); + + it('should format another date correctly', () => { + const date = new Date('2020-01-01'); + expect(formatDate(date)).toBe('January 1, 2020'); + }); + + it('should handle invalid date strings gracefully', () => { + expect(formatDate(new Date('invalid-date'))).toBe('Invalid Date'); + }); + }); +}); diff --git a/src/__test__/isAllowed.test.ts b/src/__test__/isAllowed.test.ts index 471a950..d4636f1 100644 --- a/src/__test__/isAllowed.test.ts +++ b/src/__test__/isAllowed.test.ts @@ -6,6 +6,7 @@ import { User } from '../entities/User'; import { responseError } from '../utils/response.utils'; import { v4 as uuid } from 'uuid'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { server } from '..'; jest.mock('../utils/response.utils'); @@ -19,7 +20,7 @@ const suspendedUserId = uuid(); beforeAll(async () => { const connection = await dbConnection(); - const userRepository = connection?.getRepository(User); + const userRepository = await connection?.getRepository(User); const activeUser = new User(); activeUser.id = activeUserId; @@ -49,6 +50,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase(); + server.close(); }); describe('Middleware - checkUserStatus', () => { diff --git a/src/__test__/logger.test.ts b/src/__test__/logger.test.ts new file mode 100644 index 0000000..71a766d --- /dev/null +++ b/src/__test__/logger.test.ts @@ -0,0 +1,80 @@ +import { dbConnection } from '../startups/dbConnection'; +import logger from '../utils/logger'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { server } from '../../src/index'; + +jest.mock('../utils/logger', () => ({ + __esModule: true, + default: { + log: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + http: jest.fn(), + }, +})); + +beforeAll(async () => { + const connection = await dbConnection(); +}); + +afterAll(async () => { + await cleanDatabase(); + server.close(); +}); + +describe('Logger', () => { + it('should create a logger with the correct configuration', () => { + expect(logger).toBeDefined(); + }); + + it('should log messages with the correct level and format', () => { + const testMessage = 'Test log message'; + const testLevel = 'info'; + + logger.log(testLevel, testMessage); + + expect(logger.log).toHaveBeenCalledWith(testLevel, testMessage); + }); + + it('should correctly handle info level logs', () => { + const testMessage = 'Test info message'; + + logger.info(testMessage); + + expect(logger.info).toHaveBeenCalledWith(testMessage); + }); + + it('should correctly handle warn level logs', () => { + const testMessage = 'Test warn message'; + + logger.warn(testMessage); + + expect(logger.warn).toHaveBeenCalledWith(testMessage); + }); + + it('should correctly handle error level logs', () => { + const testMessage = 'Test error message'; + + logger.error(testMessage); + + expect(logger.error).toHaveBeenCalledWith(testMessage); + }); + + it('should correctly handle debug level logs', () => { + const testMessage = 'Test debug message'; + + logger.debug(testMessage); + + expect(logger.debug).toHaveBeenCalledWith(testMessage); + }); + + it('should correctly handle http level logs', () => { + const testMessage = 'Test http message'; + + logger.http(testMessage); + + expect(logger.http).toHaveBeenCalledWith(testMessage); + }); +}); \ No newline at end of file diff --git a/src/__test__/logout.test.ts b/src/__test__/logout.test.ts index ac9eefa..2ae713c 100644 --- a/src/__test__/logout.test.ts +++ b/src/__test__/logout.test.ts @@ -72,4 +72,4 @@ describe('POST /user/logout', () => { expect(res.status).toBe(400); expect(res.body).toEqual({ Message: 'Access denied. You must be logged in' }); }); -}); +}); \ No newline at end of file diff --git a/src/__test__/product.entities.test.ts b/src/__test__/product.entities.test.ts new file mode 100644 index 0000000..5f9fc1d --- /dev/null +++ b/src/__test__/product.entities.test.ts @@ -0,0 +1,175 @@ +import { validate } from 'class-validator'; +import { Repository } from 'typeorm'; +import { Product } from '../entities/Product'; +import { User } from '../entities/User'; +import { Category } from '../entities/Category'; +import { Coupon } from '../entities/coupon'; +import { v4 as uuid } from 'uuid'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { dbConnection } from '../startups/dbConnection'; +import { server } from '../index'; + +// Sample data +const catId = uuid(); +const vendor3Id = uuid(); +const product1Id = uuid(); +const product2Id = uuid(); +const couponId1 = uuid(); +const couponId2 = uuid(); +const couponCode1 = 'DISCOUNT10'; +const couponCode2 = 'DISCOUNT20'; + +if (!process.env.TEST_USER_EMAIL + || !process.env.TEST_USER_PASS) throw new Error('TEST_USER_PASS or TEST_USER_EMAIL not set in .env'); + +const sampleVendor3 = new User(); +sampleVendor3.id = vendor3Id; +sampleVendor3.firstName = 'Vendor3'; +sampleVendor3.lastName = 'User'; +sampleVendor3.email = process.env.TEST_USER_EMAIL; +sampleVendor3.password = process.env.TEST_USER_PASS; +sampleVendor3.userType = 'Vendor'; +sampleVendor3.gender = 'Male'; +sampleVendor3.phoneNumber = '32638099634'; +sampleVendor3.photoUrl = 'https://example.com/photo.jpg'; +sampleVendor3.role = 'VENDOR'; + +const sampleProduct1 = new Product(); +sampleProduct1.id = product1Id; +sampleProduct1.name = 'Test Product 1'; +sampleProduct1.description = 'Amazing product 1'; +sampleProduct1.images = ['photo1.jpg', 'photo2.jpg', 'photo3.jpg']; +sampleProduct1.newPrice = 200; +sampleProduct1.quantity = 10; +sampleProduct1.vendor = sampleVendor3; + +const sampleProduct2 = new Product(); +sampleProduct2.id = product2Id; +sampleProduct2.name = 'Test Product 2'; +sampleProduct2.description = 'Amazing product 2'; +sampleProduct2.images = ['photo1.jpg', 'photo2.jpg', 'photo3.jpg']; +sampleProduct2.newPrice = 250; +sampleProduct2.quantity = 15; +sampleProduct2.vendor = sampleVendor3; + +const sampleCoupon1 = new Coupon(); +sampleCoupon1.id = couponId1; +sampleCoupon1.code = couponCode1; +sampleCoupon1.discountRate = 20; +sampleCoupon1.expirationDate = new Date('2025-01-01'); +sampleCoupon1.maxUsageLimit = 100; +sampleCoupon1.discountType = 'percentage'; +sampleCoupon1.product = sampleProduct1; +sampleCoupon1.vendor = sampleVendor3; + +const sampleCoupon2 = new Coupon(); +sampleCoupon2.id = couponId2; +sampleCoupon2.code = couponCode2; +sampleCoupon2.discountRate = 15; +sampleCoupon2.expirationDate = new Date('2025-01-01'); +sampleCoupon2.maxUsageLimit = 50; +sampleCoupon2.discountType = 'percentage'; +sampleCoupon2.product = sampleProduct2; +sampleCoupon2.vendor = sampleVendor3; + +const sampleCat = { + id: catId, + name: 'accessories', +}; + +let productRepository: Repository; +let userRepository: Repository; +let categoryRepository: Repository; +let couponRepository: Repository; + +beforeAll(async () => { + const connection = await dbConnection(); + if (!connection) { + console.error('Failed to connect to the database'); + return; + } + + userRepository = connection.getRepository(User); + categoryRepository = connection.getRepository(Category); + couponRepository = connection.getRepository(Coupon); + productRepository = connection.getRepository(Product); + + await userRepository.save(sampleVendor3); + await categoryRepository.save(sampleCat); + + const category1 = categoryRepository.create({ name: 'Category 1' }); + const category2 = categoryRepository.create({ name: 'Category 2' }); + await categoryRepository.save([category1, category2]); + + sampleProduct1.categories = [category1]; + sampleProduct2.categories = [category2]; + await productRepository.save([sampleProduct1, sampleProduct2]); + await couponRepository.save([sampleCoupon1, sampleCoupon2]); +}); + +afterAll(async () => { + await cleanDatabase(); + const connection = await dbConnection(); + if (connection) { + await connection.close(); + } + server.close(); +}); + +describe('Product Entity', () => { + it('should create all entities related to product entity', async () => { + const product = await productRepository.save(sampleProduct2); + expect(product).toBeDefined(); + }); + + it('should not validate a product with missing required fields', async () => { + const product = new Product(); + const errors = await validate(product); + expect(errors.length).toBeGreaterThan(0); + }); + + it('should enforce array constraints on images', async () => { + const product = productRepository.create({ + id: uuid(), + vendor: sampleVendor3, + name: 'Sample Product', + description: 'This is a sample product', + images: [], + newPrice: 100.0, + quantity: 10, + isAvailable: true, + }); + + const errors = await validate(product); + expect(errors.length).toBeGreaterThan(0); + expect(errors[0].constraints?.arrayNotEmpty).toBeDefined(); + }); + + it('should handle relationships correctly', async () => { + const category1 = await categoryRepository.findOne({ where: { name: 'Category 1' } }); + const category2 = await categoryRepository.findOne({ where: { name: 'Category 2' } }); + + const product = productRepository.create({ + id: uuid(), + vendor: sampleVendor3, + name: 'Sample Product', + description: 'This is a sample product', + images: ['image1.jpg', 'image2.jpg'], + newPrice: 100.0, + quantity: 10, + isAvailable: true, + categories: [category1 as Category, category2 as Category], + }); + + await productRepository.save(product); + + const savedProduct = await productRepository.findOne({ + where: { id: product.id }, + relations: ['vendor', 'categories', 'coupons'], + }); + + expect(savedProduct).toBeDefined(); + expect(savedProduct?.vendor).toBeDefined(); + expect(savedProduct?.categories).toBeDefined(); + }); +}); \ No newline at end of file diff --git a/src/__test__/productStatus.test.ts b/src/__test__/productStatus.test.ts index 8e8b42a..ec692f3 100644 --- a/src/__test__/productStatus.test.ts +++ b/src/__test__/productStatus.test.ts @@ -160,7 +160,15 @@ describe('Vendor product availability status management tests', () => { expect(response.statusCode).toBe(200); expect(response.body.data.message).toBe('Product status updated successfully'); - }, 10000); + }); + + it('Should return 400 if isAvailable field is not provided', async () => { + const response = await request(app) + .put(`/product/availability/${product1Id}`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.statusCode).toBe(400); + }); it('should auto update product status to false if product is expired', async () => { const response = await request(app) @@ -170,7 +178,7 @@ describe('Vendor product availability status management tests', () => { }) .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); - expect(response.statusCode).toBe(201); + expect(response.statusCode).toBe(200); expect(response.body.data.message).toBe('Product status is set to false because it is expired.'); }); @@ -182,7 +190,7 @@ describe('Vendor product availability status management tests', () => { }) .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); - expect(response.statusCode).toBe(202); + expect(response.statusCode).toBe(200); expect(response.body.data.message).toBe('Product status is set to false because it is out of stock.'); }); @@ -209,7 +217,7 @@ describe('Vendor product availability status management tests', () => { expect(response.body.message).toBe('Product not found'); }); - it('should not update product which is not in VENDOR s stock', async () => { + it('should not update product which is not in vendor`s stock', async () => { const response = await request(app) .put(`/product/availability/${product3Id}`) .send({ @@ -220,6 +228,17 @@ describe('Vendor product availability status management tests', () => { expect(response.statusCode).toBe(404); expect(response.body.message).toBe('Product not found in your stock'); }); + + it('should return error response, for incorrect id syntax (invalid uuid) ', async () => { + const response = await request(app) + .put(`/product/availability/invalid-uuid`) + .send({ + isAvailable: true, + }) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.statusCode).toBe(500); + }); }); describe('search product by name availability tests', () => { diff --git a/src/__test__/roleCheck.test.ts b/src/__test__/roleCheck.test.ts index 32df044..1ad0467 100644 --- a/src/__test__/roleCheck.test.ts +++ b/src/__test__/roleCheck.test.ts @@ -7,6 +7,7 @@ import { v4 as uuid } from 'uuid'; import { getConnection } from 'typeorm'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; + let reqMock: Partial; let resMock: Partial; let nextMock: NextFunction; diff --git a/src/__test__/route.test.ts b/src/__test__/route.test.ts index ac704b5..12eaaaa 100644 --- a/src/__test__/route.test.ts +++ b/src/__test__/route.test.ts @@ -4,6 +4,7 @@ import { createConnection, getConnection, getConnectionOptions, getRepository } import { User } from '../entities/User'; import { response } from 'express'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { v4 as uuid } from 'uuid'; beforeAll(async () => { await createConnection(); @@ -60,6 +61,25 @@ describe('POST /user/register', () => { }); }); describe('POST /user/verify/:id', () => { + + it('should not verify user, for incorrect id (invalid uuid)', async () => { + const response = await request(app) + .get(`/user/verify/invalid-uuid`); + + // Assert + expect(response.status).toBe(400); + + }); + + it('should not verify email for non existing user', async () => { + const response = await request(app) + .get(`/user/verify/${uuid()}`); + + // Assert + expect(response.status).toBe(404); + + }); + it('should verify a user', async () => { // Arrange const newUser = { @@ -188,43 +208,3 @@ describe('Password Reset Service', () => { } }); }); -describe('PUT/user/update', () => { - it('should return 401 if user is not authenticated', async () => { - const newUser = { - firstName: 'John', - lastName: 'Doe', - email: 'john.doe23@example.com', - password: 'password', - gender: 'Male', - phoneNumber: '12345678900', - userType: 'Buyer', - photoUrl: 'https://example.com/photo.jpg', - }; - - // Create a new user - const res = await request(app).post('/user/register').send(newUser); - const userRepository = getRepository(User); - - const user = await userRepository.findOne({ where: { email: newUser.email } }); - if (user) { - const updateUser = { - id: user.id, - firstName: 'Biguseers2399', - lastName: '1', - email: 'john.doe23@example.com', - gender: 'Male', - phoneNumber: '0790easdas7dsdfd76175', - photoUrl: 'photo', - }; - const res = await request(app).put('/user/update').send(updateUser); - expect(res.status).toBe(201); - expect(res.body).toEqual({ - status: 'success', - data: { - code: 201, - message: 'User Profile has successfully been updated', - }, - }); - } - }); -}); diff --git a/src/__test__/searchProduct.test.ts b/src/__test__/searchProduct.test.ts new file mode 100644 index 0000000..f217b26 --- /dev/null +++ b/src/__test__/searchProduct.test.ts @@ -0,0 +1,171 @@ +import request from 'supertest'; +import jwt from 'jsonwebtoken'; +import { app, server } from '../index'; +import { getConnection } from 'typeorm'; +import { dbConnection } from '../startups/dbConnection'; +import { User, UserInterface } from '../entities/User'; +import { v4 as uuid } from 'uuid'; +import { Product } from '../entities/Product'; +import { Category } from '../entities/Category'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; + +const vendor1Id = uuid(); +const product1Id = uuid(); +const product2Id = uuid(); +const InvalidID = uuid(); +const expiredProductId = uuid(); +const catId = uuid(); +const jwtSecretKey = process.env.JWT_SECRET || ''; + +const getAccessToken = (id: string, email: string) => { + return jwt.sign( + { + id: id, + email: email, + }, + jwtSecretKey + ); +}; + +const sampleVendor1: UserInterface = { + id: vendor1Id, + firstName: 'vendor1o', + lastName: 'user', + email: 'vendor10@example.com', + password: 'password', + userType: 'Vendor', + gender: 'Male', + phoneNumber: '126380996348', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; + +const sampleCat = { + id: catId, + name: 'accessories', +}; + +const sampleProduct1 = { + id: product1Id, + name: 'test product single', + description: 'amazing product', + images: ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'], + newPrice: 200, + quantity: 10, + vendor: sampleVendor1, + categories: [sampleCat], +}; + +const sampleProduct2 = { + id: product2Id, + name: 'another test product', + description: 'another amazing product', + images: ['photo4.jpg', 'photo5.jpg'], + newPrice: 150, + quantity: 5, + vendor: sampleVendor1, + categories: [sampleCat], +}; + +const expiredProduct = { + id: expiredProductId, + name: 'expired product', + description: 'this product is expired', + images: ['photo6.jpg'], + newPrice: 100, + quantity: 3, + vendor: sampleVendor1, + categories: [sampleCat], + expirationDate: new Date('2023-01-01'), +}; + +beforeAll(async () => { + const connection = await dbConnection(); + + const categoryRepository = connection?.getRepository(Category); + await categoryRepository?.save({ ...sampleCat }); + + const userRepository = connection?.getRepository(User); + await userRepository?.save({ ...sampleVendor1 }); + + const productRepository = connection?.getRepository(Product); + await productRepository?.save({ ...sampleProduct1 }); + await productRepository?.save({ ...sampleProduct2 }); + await productRepository?.save({ ...expiredProduct }); +}); + +afterAll(async () => { + await cleanDatabase(); + server.close(); +}); + +describe('Get single product', () => { + it('should get a single product', async () => { + const response = await request(app) + .get(`/product/${product1Id}`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + expect(response.body.status).toBe('success'); + expect(response.body.product).toBeDefined(); + expect(response.body.product.id).toBe(product1Id); + }); + + it('should return 400 if product is expired', async () => { + const response = await request(app) + .get(`/product/${expiredProductId}`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(400); + expect(response.body.status).toBe('error'); + expect(response.body.message).toBe('Product expired'); + }); + + it('should return 400 for invalid product id', async () => { + const response = await request(app) + .get(`/product/${InvalidID}`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(404); + expect(response.body.status).toBe('error'); + expect(response.body.message).toBe('Product not found'); + }); + + it('should return 404 if product not found', async () => { + const response = await request(app) + .get(`/product/${InvalidID}`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(404); + }); +}); + +describe('GET / product search', () => { + it('should sort products by newPrice in descending order', async () => { + const response = await request(app) + .get(`/product/search/`) + .query({ name: 'test', sortBy: 'newPrice', sortOrder: 'DESC' }); + + expect(response.status).toBe(200); + expect(response.body.status).toBe('success'); + expect(response.body.data).toHaveLength(2); + }); + + it('should return 400 if no name is provided', async () => { + const response = await request(app) + .get(`/product/search/`) + .query({ name: '' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Please provide a search term'); + }); + + it('should return a 404 error if no products are found', async () => { + const response = await request(app) + .get('/product/search') + .query({ name: 'nonexistentproduct' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('No products found'); + }); +}); diff --git a/src/__test__/user.entity.test.ts b/src/__test__/user.entity.test.ts new file mode 100644 index 0000000..7179131 --- /dev/null +++ b/src/__test__/user.entity.test.ts @@ -0,0 +1,277 @@ +import { validate } from 'class-validator'; +import { getConnection, Repository } from 'typeorm'; +import { User, UserInterface } from '../entities/User'; +import { Order } from '../entities/Order'; +import { Transaction } from '../entities/transaction'; +import { Product } from '../entities/Product'; +import { OrderItem } from '../entities/OrderItem'; +import { VendorOrderItem } from '../entities/VendorOrderItem'; +import { VendorOrders } from '../entities/vendorOrders'; +import { Category } from '../entities/Category'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { server } from '../index'; +import { v4 as uuid } from 'uuid'; +import { dbConnection } from '../startups/dbConnection'; + +const adminId = uuid(); +const vendorId = uuid(); +const vendor2Id = uuid(); +const buyerId = uuid(); +const productId = uuid(); +const orderId = uuid(); +const order2Id = uuid(); +const orderItemId = uuid(); +const vendorOrderId = uuid(); +const vendorOrderItemId = uuid(); +const vendorOrder2Id = uuid(); +const catId = uuid(); + +if (!process.env.TEST_USER_EMAIL + || !process.env.TEST_BUYER_EMAIL + || !process.env.TEST_VENDOR1_EMAIL + || !process.env.TEST_VENDOR_EMAIL + || !process.env.TEST_USER_PASS) throw new Error('TEST_USER_PASS or TEST_USER_EMAIL not set in .env'); + +const sampleAdmin: UserInterface = { + id: adminId, + firstName: 'admin', + lastName: 'user', + email:process.env.TEST_USER_EMAIL, + password: process.env.TEST_USER_PASS, + userType: 'Admin', + gender: 'Male', + phoneNumber: '126380997', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'ADMIN', +}; +const sampleVendor: UserInterface = { + id: vendorId, + firstName: 'vendor', + lastName: 'user', + email:process.env.TEST_VENDOR_EMAIL, + password: process.env.TEST_USER_PASS, + userType: 'Vendor', + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'VENDOR', +}; +const sampleVendor2: UserInterface = { + id: vendor2Id, + firstName: 'vendor', + lastName: 'user', + email: process.env.TEST_VENDOR1_EMAIL, + password: process.env.TEST_USER_PASS, + userType: 'Vendor', + gender: 'Male', + phoneNumber: '18090296347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'VENDOR', +}; +const sampleBuyer: UserInterface = { + id: buyerId, + firstName: 'buyer', + lastName: 'user', + email: process.env.TEST_BUYER_EMAIL, + password: process.env.TEST_USER_PASS, + userType: 'Buyer', + gender: 'Male', + phoneNumber: '6380996347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'BUYER', +}; +const sampleCat = { + id: catId, + name: 'accessories', +}; + +const sampleProduct = { + id: productId, + name: 'test product', + description: 'amazing product', + images: ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'], + newPrice: 200, + quantity: 10, + vendor: sampleVendor, + categories: [sampleCat], +}; +const sampleOrder = { + id: orderId, + totalPrice: 400, + quantity: 2, + orderDate: new Date(), + buyer: sampleBuyer, + orderStatus: 'received', + address: 'Rwanda, Kigali, KK20st', +}; +const sampleOrderItem = { + id: orderItemId, + price: 200, + quantity: 2, + order: sampleOrder, + product: sampleProduct, +}; +const sampleVendorOrder = { + id: vendorOrderId, + totalPrice: 400, + quantity: 2, + vendor: sampleVendor, + order: sampleOrder, + buyer: sampleBuyer, + orderStatus: 'pending', +}; +const sampleVendorOrderItem = { + id: vendorOrderItemId, + "price/unit": 200, + quantity: 2, + order: sampleVendorOrder, + product: sampleProduct, +}; + +let userRepository: Repository; +let orderRepository: Repository; +let transactionRepository: Repository; + +beforeAll(async () => { + const connection = await dbConnection(); + if (!connection) { + console.error('Failed to connect to the database'); + return; + } + + userRepository = connection.getRepository(User); + orderRepository = connection.getRepository(Order); + transactionRepository = connection.getRepository(Transaction); + + const categoryRepository = connection.getRepository(Category); + await categoryRepository.save(sampleCat); + + await userRepository.save([sampleAdmin, sampleVendor, sampleVendor2, sampleBuyer]); + + const productRepository = connection.getRepository(Product); + await productRepository.save(sampleProduct); + + await orderRepository.save(sampleOrder); + + const orderItemRepository = connection.getRepository(OrderItem); + await orderItemRepository.save(sampleOrderItem); + + const vendorOrderRepository = connection.getRepository(VendorOrders); + await vendorOrderRepository.save(sampleVendorOrder); + + const vendorOrderItemRepository = connection.getRepository(VendorOrderItem); + await vendorOrderItemRepository.save(sampleVendorOrderItem); +}); + +afterAll(async () => { + await cleanDatabase(); + const connection = getConnection(); + if (connection.isConnected) { + await connection.close(); + } + server.close(); +}); + +describe('User Entity', () => { + it('should validate a valid user', async () => { + const user = userRepository.create({ + id: uuid(), + firstName: 'John', + lastName: 'Doe', + email: process.env.TEST_SAMPLE_BUYER_EMAIL, + password: process.env.TEST_USER_PASS, + gender: 'male', + phoneNumber: '1234567890', + verified: true, + status: 'active', + userType: 'Buyer', + twoFactorEnabled: false, + accountBalance: 0.0, + }); + + const errors = await validate(user); + expect(errors.length).toBe(0); + + const savedUser = await userRepository.save(user); + expect(savedUser.id).toBeDefined(); + expect(savedUser.createdAt).toBeDefined(); + expect(savedUser.updatedAt).toBeDefined(); + }); + + it('should not validate a user with missing required fields', async () => { + const user = new User(); + + const errors = await validate(user); + expect(errors.length).toBeGreaterThan(0); + }); + + it('should set the role based on userType', async () => { + const user = userRepository.create({ + id: vendorOrder2Id, + firstName: 'Jane', + lastName: 'Doe', + email: process.env.TEST_VENDOR2_EMAIL, + password: process.env.TEST_USER_PASS, + gender: 'female', + phoneNumber: '0987654321', + userType: 'Vendor', + }); + + await userRepository.save(user); + expect(user.role).toBe('VENDOR'); + }); + + it('should handle relationships correctly', async () => { + const user = userRepository.create({ + id: uuid(), + firstName: 'Alice', + lastName: 'Smith', + email: 'alice.smith@example.com', + password: process.env.TEST_USER_PASS, + gender: 'female', + phoneNumber: '1122334455', + userType: 'Buyer', + }); + + const savedUser = await userRepository.save(user); + + const order = orderRepository.create({ + id: order2Id, + totalPrice: 400, + quantity: 2, + orderDate: new Date(), + buyer: savedUser, + orderStatus: 'order placed', + address: 'Rwanda, Kigali, KK20st', + }); + + const savedOrder = await orderRepository.save(order); + + const transaction = transactionRepository.create({ + id: uuid(), + order: savedOrder, + user: savedUser, + product: sampleProduct, + amount: 400, + previousBalance: 0, + currentBalance: 400, + type: 'credit', + description: 'order placed', + }); + + await transactionRepository.save(transaction); + + const foundUser = await userRepository.findOne({ + where: { id: savedUser.id }, + relations: ['orders', 'transactions'], + }); + + expect(foundUser).toBeDefined(); + expect(foundUser?.orders.length).toBe(1); + expect(foundUser?.transactions.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/src/__test__/userServices.test.ts b/src/__test__/userServices.test.ts index 29a2e7c..a58dff7 100644 --- a/src/__test__/userServices.test.ts +++ b/src/__test__/userServices.test.ts @@ -1,11 +1,102 @@ import request from 'supertest'; import { app, server } from '../index'; import { createConnection, getRepository } from 'typeorm'; -import { User } from '../entities/User'; +import { User, UserInterface } from '../entities/User'; + import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { v4 as uuid } from 'uuid'; +import { dbConnection } from '../startups/dbConnection'; + +import bcrypt from 'bcrypt' +import jwt from 'jsonwebtoken'; + +const userId = uuid(); +const user1Id = uuid(); +const user2Id = uuid(); +const user3Id = uuid(); + +const getAccessToken = (id: string, email: string) => { + return jwt.sign( + { + id: id, + email: email, + }, + process.env.JWT_SECRET || '' + ); +}; + +const sampleUser: UserInterface = { + id: userId, + firstName: 'user', + lastName: 'user', + email: 'user@example.com', + password: '', + userType: 'Vendor', + verified: true, + gender: 'Male', + phoneNumber: '12638096347', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; + +const sampleUser1: UserInterface = { + id: user1Id, + firstName: 'user1', + lastName: 'user', + email: 'user1@example.com', + password: 'password', + userType: 'Vendor', + verified: true, + status: 'suspended', + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; + +const sampleUser2: UserInterface = { + id: user2Id, + firstName: 'user2', + lastName: 'user', + email: 'user2@example.com', + password: '', + userType: 'Vendor', + verified: true, + twoFactorEnabled: true, + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; + +const sampleUser3: UserInterface = { + id: user3Id, + firstName: 'user3', + lastName: 'user', + email: 'user3@example.com', + password: '', + userType: 'Vendor', + verified: true, + twoFactorEnabled: true, + twoFactorCode: '123456', + twoFactorCodeExpiresAt: new Date(Date.now() + 10 * 60 * 1000), + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; beforeAll(async () => { - await createConnection(); + const connection = await dbConnection(); + sampleUser.password = await bcrypt.hash('password', 10); + sampleUser2.password = await bcrypt.hash('password', 10); + sampleUser3.password = await bcrypt.hash('password', 10); + + const userRepository = connection?.getRepository(User); + await userRepository?.save({ ...sampleUser }); + await userRepository?.save({ ...sampleUser1 }); + await userRepository?.save({ ...sampleUser2 }); + await userRepository?.save({ ...sampleUser3 }); }); afterAll(async () => { @@ -13,218 +104,429 @@ afterAll(async () => { server.close(); }); -describe('start2FAProcess', () => { - it('should register a new user', async () => { - // Arrange - const newUser = { - firstName: 'John', - lastName: 'Doe', - email: 'john.doe1@example.com', - password: 'password', - gender: 'Male', - phoneNumber: '0789412421', - userType: 'Buyer', - }; - - // Act - const res = await request(app).post('/user/register').send(newUser); - // Assert - expect(res.status).toBe(201); - expect(res.body).toEqual({ - status: 'success', - data: { - code: 201, - message: 'User registered successfully', - }, +describe('User service Test', () => { + describe('User Authentication', () => { + it('should register a new user', async () => { + // Arrange + const newUser = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe1@example.com', + password: 'password', + gender: 'Male', + phoneNumber: '0789412421', + userType: 'Buyer', + }; + + // Act + const res = await request(app).post('/user/register').send(newUser); + // Assert + expect(res.status).toBe(201); + expect(res.body).toEqual({ + status: 'success', + data: { + code: 201, + message: 'User registered successfully', + }, + }); }); - }); - it('should return 400 if not sent email in body on enabling 2fa', async () => { - const data = {}; + it('should Login a user, with valid credentials', async () => { + const res = await request(app) + .post('/user/login') + .send({ + email: 'user@example.com', + password: 'password', + }); + // Assert + expect(res.status).toBe(200); + expect(res.body.data.token).toBeDefined(); + }); - const res = await request(app).post('/user/enable-2fa').send(data); + it('should send OTP, if 2FA is enabled', async () => { + const res = await request(app) + .post('/user/login') + .send({ + email: 'user2@example.com', + password: 'password', + }); + // Assert + expect(res.status).toBe(200); + expect(res.body.data.message).toBe('Please provide the OTP sent to your email or phone'); + }); - expect(res.status).toBe(400); - expect(res.body).toEqual({ status: 'error', message: 'Please provide your email' }); - }); + it('should not Login a user, with invalid credentials', async () => { + const res = await request(app) + .post('/user/login') + .send({ + email: 'user@example.com', + password: 'passwo', + }); + // Assert + expect(res.status).toBe(401); + }); - it('should return 404 if user not exist on enabling 2fa', async () => { - const data = { - email: 'example@gmail.com', - }; + it('should not login User if user email is not verified', async () => { + const res = await request(app) + .post('/user/login') + .send({ + email: 'john.doe1@example.com', + password: 'password', + }); - const res = await request(app).post('/user/enable-2fa').send(data); + expect(res.status).toBe(400); + expect(res.body.message).toBe("Please verify your account"); + }); - expect(res.status).toBe(404); - expect(res.body).toEqual({ status: 'error', message: 'User not found' }); + it('should not login User if user is currently suspended', async () => { + const res = await request(app) + .post('/user/login') + .send({ + email: sampleUser1.email, + password: sampleUser1.password, + }); + + expect(res.status).toBe(400); + expect(res.body.message).toBe("Your account has been suspended"); + }); }); - it('should enable two-factor authentication', async () => { - const data = { - email: 'john.doe1@example.com', - }; + describe('User Profile update', () => { + it('should update user profile', async () => { + const res = await request(app) + .put('/user/update') + .send({ + firstName: 'John', + lastName: 'Doe', + gender: 'Male', + phoneNumber: '0789412421', + photoUrl: 'photo.jpg', + }) + .set('Authorization', `Bearer ${getAccessToken(userId, sampleUser.email)}`); + + expect(res.status).toBe(200); + }); + + it('should not update user profile, if there is no request body sent', async () => { + const res = await request(app) + .put('/user/update') + .set('Authorization', `Bearer ${getAccessToken(userId, sampleUser.email)}`); + + expect(res.status).toBe(400); + }); - const res = await request(app).post('/user/enable-2fa').send(data); + it('should not update user profile, when some required fields are not provided', async () => { + const res = await request(app) + .put('/user/update') + .send({ + firstName: 'firstName updated', + lastName: 'lastName updated' + }) + .set('Authorization', `Bearer ${getAccessToken(userId, sampleUser.email)}`); - expect(res.status).toBe(200); - expect(res.body).toEqual({ status: 'success', message: 'Two factor authentication enabled successfully' }); + expect(res.status).toBe(400); + }); }); - it('should return 400 if not sent email in body on disabling 2fa', async () => { - const data = {}; + describe('User Reset Password', () => { + it('should return response error, if no email and userID provided', async () => { + const respond = await request(app) + .post('/user/password/reset'); - const res = await request(app).post('/user/disable-2fa').send(data); + expect(respond.status).toBe(400); + }); - expect(res.status).toBe(400); - expect(res.body).toEqual({ status: 'error', message: 'Please provide your email' }); - }); + it('should not reset password, for no existing Users', async () => { + const respond = await request(app) + .post('/user/password/reset') + .query({ + email: 'example@gmail.com', + userid: uuid() + }); - it('should return 404 if user not exist on disabling 2fa', async () => { - const data = { - email: 'example@gmail.com', - }; + expect(respond.status).toBe(404); + }); - const res = await request(app).post('/user/disable-2fa').send(data); + it('should not reset password, if no new password sent', async () => { + const respond = await request(app) + .post('/user/password/reset') + .query({ + email: sampleUser.email, + userid: sampleUser.id + }); - expect(res.status).toBe(404); - expect(res.body).toEqual({ status: 'error', message: 'User not found' }); - }); + expect(respond.status).toBe(400); + expect(respond.body.message).toBe('Please provide all required fields'); + }); - it('should disable two-factor authentication', async () => { - const data = { - email: 'john.doe1@example.com', - }; + it('should not reset password, if new password doesn\'t match password in confirmPassword field', async () => { + const respond = await request(app) + .post('/user/password/reset') + .query({ + email: sampleUser.email, + userid: sampleUser.id + }) + .send({ + newPassword: 'new-password', + confirmPassword: 'password' + }); + + expect(respond.status).toBe(400); + expect(respond.body.message).toBe('new password must match confirm password'); + }); - const res = await request(app).post('/user/disable-2fa').send(data); + it('should not reset password, for incorrect user id syntax (invalid uuid)', async () => { + const respond = await request(app) + .post('/user/password/reset') + .query({ + email: sampleUser.email, + userid: 'invalid-=uuid' + }); - expect(res.status).toBe(200); - expect(res.body).toEqual({ status: 'success', message: 'Two factor authentication disabled successfully' }); + expect(respond.status).toBe(500); + }); + it('should reset password for the user', async () => { + const respond = await request(app) + .post('/user/password/reset') + .query({ + email: sampleUser.email, + userid: sampleUser.id + }) + .send({ + newPassword: 'new-password', + confirmPassword: 'new-password' + }); + + expect(respond.status).toBe(200); + expect(respond.body.data.message).toBe('Password updated successfully'); + }); }); - it('should return 400 if not sent email and otp in body on verifying OTP', async () => { - const data = {}; + describe('User password reset link', () => { + it('should send link to reset password', async () => { + const response = await request(app) + .post('/user/password/reset/link') + .query({ email: sampleUser.email }); - const res = await request(app).post('/user/verify-otp').send(data); + expect(response.status).toBe(200); + }); - expect(res.status).toBe(400); - expect(res.body).toEqual({ status: 'error', message: 'Please provide an email and OTP code' }); - }); + it('should not send link to reset password, if no email provided', async () => { + const response = await request(app) + .post('/user/password/reset/link'); - it('should return 403 if OTP is invalid', async () => { - const email = 'john.doe1@example.com'; - const user = await getRepository(User).findOneBy({ email }); - if (user) { - user.twoFactorEnabled = true; - user.twoFactorCode = '123456'; - await getRepository(User).save(user); - } - - const data = { - email: 'john.doe1@example.com', - otp: '123457', - }; - - const res = await request(app).post('/user/verify-otp').send(data); - expect(res.status).toBe(403); - expect(res.body).toEqual({ status: 'error', message: 'Invalid authentication code' }); - }); + expect(response.status).toBe(400); + expect(response.body.message).toBe('Missing required field'); + }); - it('should return 403 if user not exist on verifying OTP', async () => { - const data = { - email: 'john.doe10@example.com', - otp: '123457', - }; + it('should not send link to reset password, if email doesn\'t exist in DB', async () => { + const response = await request(app) + .post('/user/password/reset/link') + .query({ email: 'nonexistingemail@gmail.com' }); - const res = await request(app).post('/user/verify-otp').send(data); - expect(res.status).toBe(403); - expect(res.body).toEqual({ status: 'error', message: 'User not found' }); + expect(response.status).toBe(404); + expect(response.body.message).toBe('User not found'); + }); }); - it('should return 403 if OTP is expired', async () => { - const email = 'john.doe1@example.com'; - const userRepository = getRepository(User); - const user = await userRepository.findOneBy({ email }); - if (user) { - user.twoFactorEnabled = true; - user.twoFactorCode = '123456'; - user.twoFactorCodeExpiresAt = new Date(Date.now() - 10 * 60 * 1000); - await getRepository(User).save(user); - } - - const data = { - email: email, - otp: '123456', - }; - - const res = await request(app).post('/user/verify-otp').send(data); - expect(res.status).toBe(403); - expect(res.body).toEqual({ status: 'error', message: 'Authentication code expired' }); - if (user) { - await userRepository.remove(user); - } - }); + describe('Start@FAProcess', () => { - it('should return 400 if not sent email in body on resending OTP', async () => { - const data = {}; + it('should return 400 if not sent email in body on enabling 2fa', async () => { + const data = {}; - const res = await request(app).post('/user/resend-otp').send(data); + const res = await request(app).post('/user/enable-2fa').send(data); - expect(res.status).toBe(400); - expect(res.body).toEqual({ status: 'error', message: 'Please provide an email' }); - }); + expect(res.status).toBe(400); + expect(res.body).toEqual({ status: 'error', message: 'Please provide your email' }); + }); - it('should return 404 if user not exist on resending OTP', async () => { - const data = { - email: 'john.doe10@example.com', - }; + it('should return 404 if user not exist on enabling 2fa', async () => { + const data = { + email: 'example@gmail.com', + }; - const res = await request(app).post('/user/resend-otp').send(data); - expect(res.status).toBe(404); - expect(res.body).toEqual({ status: 'error', message: 'Incorrect email' }); - }); + const res = await request(app).post('/user/enable-2fa').send(data); + + expect(res.status).toBe(404); + expect(res.body).toEqual({ status: 'error', message: 'User not found' }); + }); + + it('should enable two-factor authentication', async () => { + const data = { + email: 'john.doe1@example.com', + }; + + const res = await request(app).post('/user/enable-2fa').send(data); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ status: 'success', message: 'Two factor authentication enabled successfully' }); + }); + + it('should return 400 if not sent email in body on disabling 2fa', async () => { + const data = {}; + + const res = await request(app).post('/user/disable-2fa').send(data); + + expect(res.status).toBe(400); + expect(res.body).toEqual({ status: 'error', message: 'Please provide your email' }); + }); + + it('should return 404 if user not exist on disabling 2fa', async () => { + const data = { + email: 'example@gmail.com', + }; + + const res = await request(app).post('/user/disable-2fa').send(data); + + expect(res.status).toBe(404); + expect(res.body).toEqual({ status: 'error', message: 'User not found' }); + }); + + it('should disable two-factor authentication', async () => { + const data = { + email: 'john.doe1@example.com', + }; + + const res = await request(app).post('/user/disable-2fa').send(data); - it('should resend OTP', async () => { - const newUser = { - firstName: 'John', - lastName: 'Doe', - email: 'john.doe187@example.com', - password: 'password', - gender: 'Male', - phoneNumber: '0785044398', - userType: 'Buyer', - }; - - // Act - const resp = await request(app).post('/user/register').send(newUser); - if (!resp) { - console.log('Error creating user in resend otp test case'); - } - const data = { - email: 'john.doe187@example.com', - }; - - const res = await request(app).post('/user/resend-otp').send(data); - expect(res.status).toBe(200); - expect(res.body).toEqual({ status: 'success', data: { message: 'OTP sent successfully' } }); - }, 20000); - - it('should return 400 if not sent email in body on login', async () => { - const data = {}; - - const res = await request(app).post('/user/login').send(data); - - expect(res.status).toBe(400); - expect(res.body).toEqual({ status: 'error', message: 'Please provide an email and password' }); - }, 1000); - - it('should return 404 if user not exist on login', async () => { - const data = { - email: 'john.doe10@example.com', - password: 'password', - }; - - const res = await request(app).post('/user/login').send(data); - expect(res.status).toBe(404); - expect(res.body).toEqual({ status: 'error', message: 'Incorrect email or password' }); - }, 10000); + expect(res.status).toBe(200); + expect(res.body).toEqual({ status: 'success', message: 'Two factor authentication disabled successfully' }); + }); + + it('should return 400 if not sent email and otp in body on verifying OTP', async () => { + const data = {}; + + const res = await request(app).post('/user/verify-otp').send(data); + + expect(res.status).toBe(400); + expect(res.body).toEqual({ status: 'error', message: 'Please provide an email and OTP code' }); + }); + + it('should return 403 if OTP is invalid', async () => { + const email = 'john.doe1@example.com'; + const user = await getRepository(User).findOneBy({ email }); + if (user) { + user.twoFactorEnabled = true; + user.twoFactorCode = '123456'; + await getRepository(User).save(user); + } + + const data = { + email: 'john.doe1@example.com', + otp: '123457', + }; + + const res = await request(app).post('/user/verify-otp').send(data); + expect(res.status).toBe(403); + expect(res.body).toEqual({ status: 'error', message: 'Invalid authentication code' }); + }); + + it('should return 403 if user not exist on verifying OTP', async () => { + const data = { + email: 'john.doe10@example.com', + otp: '123457', + }; + + const res = await request(app).post('/user/verify-otp').send(data); + expect(res.status).toBe(403); + expect(res.body).toEqual({ status: 'error', message: 'User not found' }); + }); + + it('should return 403 if OTP is expired', async () => { + const email = 'john.doe1@example.com'; + const userRepository = getRepository(User); + const user = await userRepository.findOneBy({ email }); + if (user) { + user.twoFactorEnabled = true; + user.twoFactorCode = '123456'; + user.twoFactorCodeExpiresAt = new Date(Date.now() - 10 * 60 * 1000); + await getRepository(User).save(user); + } + + const data = { + email: email, + otp: '123456', + }; + + const res = await request(app).post('/user/verify-otp').send(data); + expect(res.status).toBe(403); + expect(res.body).toEqual({ status: 'error', message: 'Authentication code expired' }); + }); + + it('should login user, if OTP provided is valid', async () => { + const res = await request(app) + .post('/user/verify-otp') + .send({ + email: sampleUser3.email, + otp: '123456', + }); + + expect(res.status).toBe(200); + expect(res.body.data.token).toBeDefined(); + }); + + it('should return 400 if not sent email in body on resending OTP', async () => { + const data = {}; + + const res = await request(app).post('/user/resend-otp').send(data); + + expect(res.status).toBe(400); + expect(res.body).toEqual({ status: 'error', message: 'Please provide an email' }); + }); + + it('should return 404 if user not exist on resending OTP', async () => { + const data = { + email: 'john.doe10@example.com', + }; + + const res = await request(app).post('/user/resend-otp').send(data); + expect(res.status).toBe(404); + expect(res.body).toEqual({ status: 'error', message: 'Incorrect email' }); + }); + + it('should resend OTP', async () => { + const newUser = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe187@example.com', + password: 'password', + gender: 'Male', + phoneNumber: '0785044398', + userType: 'Buyer', + }; + + // Act + const resp = await request(app).post('/user/register').send(newUser); + if (!resp) { + console.log('Error creating user in resend otp test case'); + } + const data = { + email: 'john.doe187@example.com', + }; + + const res = await request(app).post('/user/resend-otp').send(data); + expect(res.status).toBe(200); + expect(res.body).toEqual({ status: 'success', data: { message: 'OTP sent successfully' } }); + }); + + it('should return 400 if not sent email in body on login', async () => { + const data = {}; + + const res = await request(app).post('/user/login').send(data); + + expect(res.status).toBe(400); + expect(res.body).toEqual({ status: 'error', message: 'Please provide an email and password' }); + }, 1000); + + it('should return 404 if user not exist on login', async () => { + const data = { + email: 'john.doe10@example.com', + password: 'password', + }; + + const res = await request(app).post('/user/login').send(data); + expect(res.status).toBe(404); + expect(res.body).toEqual({ status: 'error', message: 'Incorrect email or password' }); + }, 10000); + }); }); diff --git a/src/__test__/userStatus.test.ts b/src/__test__/userStatus.test.ts index 69e892a..a2e0732 100644 --- a/src/__test__/userStatus.test.ts +++ b/src/__test__/userStatus.test.ts @@ -68,7 +68,7 @@ describe('POST /user/deactivate', () => { .send({ email: `${testUser.email}` }); expect(response.status).toBe(200); expect(response.body.message).toBe('User deactivated successfully'); - }, 10000); + }, 60000); it('should return 404 when email is not submitted', async () => { const token = jwt.sign(data, jwtSecretKey); @@ -111,7 +111,7 @@ describe('POST /user/activate', () => { expect(response.status).toBe(200); expect(response.body.message).toBe('User activated successfully'); - }, 10000); + }, 60000); it('should return 404 when email is not submitted', async () => { const token = jwt.sign(data, jwtSecretKey); @@ -148,4 +148,4 @@ describe('POST /user/activate', () => { expect(response.status).toBe(404); expect(response.body.error).toBe('User not found'); }); -}); +}); \ No newline at end of file diff --git a/src/__test__/vendorProduct.test.ts b/src/__test__/vendorProduct.test.ts index d8fc0a5..54fec95 100644 --- a/src/__test__/vendorProduct.test.ts +++ b/src/__test__/vendorProduct.test.ts @@ -133,7 +133,24 @@ describe('Vendor product management tests', () => { expect(response.status).toBe(201); expect(response.body.data.product).toBeDefined; - }, 60000); + }); + + it('should create new product. Test when provided one category and currently doesn\'t exist in DB ', async () => { + const response = await request(app) + .post('/product') + .field('name', 'test product4') + .field('description', 'amazing product4') + .field('newPrice', 200) + .field('quantity', 50) + .field('expirationDate', '10-2-2023') + .field('categories', 'new-category') + .attach('images', `${__dirname}/test-assets/photo1.png`) + .attach('images', `${__dirname}/test-assets/photo2.webp`) + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(201); + expect(response.body.data.product).toBeDefined; + }); it('return an error if the number of product images exceeds 6', async () => { const response = await request(app) @@ -203,7 +220,7 @@ describe('Vendor product management tests', () => { }); describe('Updating existing product', () => { - it('return error, if there are missing field data', async () => { + it('return response error, if there are missing field data', async () => { const response = await request(app) .put(`/product/${sampleProduct2.id}`) .field('newPrice', 200) @@ -218,7 +235,7 @@ describe('Vendor product management tests', () => { expect(response.status).toBe(400); }); - it('return error, if product do not exist', async () => { + it('return response error, if product do not exist', async () => { const response = await request(app) .put(`/product/${uuid()}`) .field('name', 'test product3') @@ -235,8 +252,23 @@ describe('Vendor product management tests', () => { expect(response.status).toBe(404); expect(response.body.error).toBe('Product not found'); }); + + it('return response error, for incorrect product id syntax (invalid uuid)', async () => { + const response = await request(app) + .put(`/product/invalid uuid`) + .field('name', 'test product3') + .field('description', 'amazing product3') + .field('newPrice', 200) + .field('quantity', 10) + .field('expirationDate', '10-2-2023') + .field('categories', 'technology') + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); - it('return an error if the number of product images exceeds 6', async () => { + expect(response.status).toBe(400); + expect(response.body.message).toBe('invalid input syntax for type uuid: "invalid uuid"'); + }); + + it('return response error if the number of product images exceeds 6', async () => { const response = await request(app) .put(`/product/${sampleProduct2.id}`) .field('name', 'test product3') @@ -255,6 +287,21 @@ describe('Vendor product management tests', () => { expect(response.body.error).toBe('Product cannot have more than 6 images'); }); + it('should update the product. Test when provided one category and currently doesn\'t exist in DB ', async () => { + const response = await request(app) + .put(`/product/${sampleProduct2.id}`) + .field('name', 'test product3 updated') + .field('description', 'amazing product3') + .field('newPrice', 200) + .field('quantity', 50) + .field('expirationDate', '10-2-2023') + .field('categories', 'new-category-update') + .set('Authorization', `Bearer ${getAccessToken(vendor1Id, sampleVendor1.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Product updated successfully'); + }); + it('should update the product', async () => { const response = await request(app) .put(`/product/${sampleProduct2.id}`) @@ -425,14 +472,30 @@ describe('Vendor product management tests', () => { expect(response.status).toBe(200); expect(response.body.data).toBeDefined; }); + + it('should retrieve products, according to categories and vendor', async () => { + const response = await request(app) + .get('/product/recommended') + .query({ + categories: sampleCat.id, + vendor: sampleVendor1.id + }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + expect(response.body.data.products).toBeDefined; + }); it('should not return any product for a vendor with zero product in stock', async () => { const response = await request(app) - .get(`/product/recommended`) + .get('/product/recommended') + .query({ + categories: sampleCat.id, + vendor: sampleVendor2.id + }) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(200); - expect(response.body.products).toBeUndefined; + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('No products found for the specified vendor'); }); it('should not return any product for incorrect syntax of input', async () => { diff --git a/src/__test__/wishList.test.ts b/src/__test__/wishList.test.ts index 6658853..23f2609 100644 --- a/src/__test__/wishList.test.ts +++ b/src/__test__/wishList.test.ts @@ -201,4 +201,4 @@ describe('Wish list management tests', () => { expect(response.body.message).toBe('All products removed successfully'); }); }); -}); +}); \ No newline at end of file diff --git a/src/controllers/productController.ts b/src/controllers/productController.ts index 05aa5a3..d24e1e5 100644 --- a/src/controllers/productController.ts +++ b/src/controllers/productController.ts @@ -52,25 +52,9 @@ export const singleProduct = async (req: Request, res: Response) => { await viewSingleProduct(req, res); }; export const searchProduct = async (req: Request, res: Response) => { - const { name, sortBy, sortOrder, page, limit } = req.query; + await searchProductService (req, res); - try { - const searchParams = { - name: name as string, - sortBy: sortBy as string, - sortOrder: sortOrder as 'ASC' | 'DESC', - page: parseInt(page as string, 10) || 1, - limit: parseInt(limit as string, 10) || 10, - }; - - const result = await searchProductService(searchParams); - - res.json(result); - } catch (error) { - console.error('Error searching products:', error); - res.status(500).json({ error: 'Internal Server Error' }); - } }; export const Payment = async (req: Request, res: Response) => { await confirmPayment(req, res); -}; +}; \ No newline at end of file diff --git a/src/entities/User.ts b/src/entities/User.ts index 232ce11..787c5e0 100644 --- a/src/entities/User.ts +++ b/src/entities/User.ts @@ -28,6 +28,9 @@ export interface UserInterface { status?: 'active' | 'suspended'; userType: 'Admin' | 'Buyer' | 'Vendor'; role?: string; + twoFactorEnabled?: boolean; + twoFactorCode?: string; + twoFactorCodeExpiresAt?: Date; createdAt?: Date; updatedAt?: Date; } @@ -119,4 +122,4 @@ export class User { setRole (): void { this.role = this.userType === 'Vendor' ? roles.vendor : roles.buyer; } -} +} \ No newline at end of file diff --git a/src/helper/couponValidator.ts b/src/helper/couponValidator.ts index 9736aa8..a82865b 100644 --- a/src/helper/couponValidator.ts +++ b/src/helper/couponValidator.ts @@ -36,6 +36,9 @@ export const validateCouponUpdate = ( code: Joi.string().min(5).messages({ 'string.min': 'code must be at least 5 characters long.', }), + product: Joi.string().messages({ + 'any.required': 'product is required.', + }), discountRate: Joi.number().messages({ 'number.base': 'discountRate must be a number.', }), diff --git a/src/middlewares/isAllowed.ts b/src/middlewares/isAllowed.ts index 77c115b..afaa0a9 100644 --- a/src/middlewares/isAllowed.ts +++ b/src/middlewares/isAllowed.ts @@ -20,12 +20,6 @@ export interface UserInterface { updatedAt: Date; } -declare module 'express' { - interface Request { - user?: Partial; - } -} - export const checkUserStatus = async (req: Request, res: Response, next: NextFunction) => { try { if (!req.user) { diff --git a/src/routes/ProductRoutes.ts b/src/routes/ProductRoutes.ts index b2af1a5..49a6c5e 100644 --- a/src/routes/ProductRoutes.ts +++ b/src/routes/ProductRoutes.ts @@ -27,6 +27,8 @@ import { updateBuyerVendorOrder, } from '../controllers'; const router = Router(); + +router.get('/search', searchProduct); router.get('/all', listAllProducts); router.get('/recommended', authMiddleware as RequestHandler, hasRole('BUYER'), getRecommendedProducts); router.get('/collection', authMiddleware as RequestHandler, hasRole('VENDOR'), readProducts); @@ -56,4 +58,5 @@ router.get('/admin/orders/:id', authMiddleware as RequestHandler, hasRole('ADMIN router.put('/admin/orders/:id', authMiddleware as RequestHandler, hasRole('ADMIN'), updateBuyerVendorOrder); router.post('/payment/:id', authMiddleware as RequestHandler, hasRole('BUYER'), Payment); + export default router; diff --git a/src/routes/UserRoutes.ts b/src/routes/UserRoutes.ts index 79b0551..ad25a36 100644 --- a/src/routes/UserRoutes.ts +++ b/src/routes/UserRoutes.ts @@ -1,4 +1,4 @@ -import { Router } from 'express'; +import { RequestHandler, Router } from 'express'; import { responseError } from '../utils/response.utils'; import { UserInterface } from '../entities/User'; import jwt from 'jsonwebtoken'; @@ -20,6 +20,7 @@ import { hasRole } from '../middlewares/roleCheck'; import { isTokenValide } from '../middlewares/isValid'; import passport from 'passport'; import '../utils/auth'; +import { authMiddleware } from '../middlewares/verifyToken'; const router = Router(); router.post('/register', userRegistration); @@ -34,7 +35,7 @@ router.post('/activate', isTokenValide, hasRole('ADMIN'), activateUser); router.post('/deactivate', isTokenValide, hasRole('ADMIN'), disactivateUser); router.post('/password/reset', userPasswordReset); router.post('/password/reset/link', sendPasswordResetLink); -router.put('/update', userProfileUpdate); +router.put('/update', authMiddleware as RequestHandler, userProfileUpdate); router.get('/google-auth', passport.authenticate('google', { scope: ['profile', 'email'] })); router.get( diff --git a/src/routes/index.ts b/src/routes/index.ts index af462b4..75b31ad 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from 'express'; -import { responseSuccess } from '../utils/response.utils'; +import { Request, RequestHandler, Response, Router } from 'express'; +import { responseServerError, responseSuccess } from '../utils/response.utils'; import userRoutes from './UserRoutes'; import productRoutes from './ProductRoutes'; import wishListRoutes from './wishListRoute'; @@ -7,6 +7,7 @@ import couponRoute from './couponRoutes'; import cartRoutes from './CartRoutes'; import feedbackRoute from './feedbackRoutes'; import notificationRoute from './NoficationRoutes' +import { authMiddleware } from '../middlewares/verifyToken'; import chatBot from './chatBot'; const router = Router(); @@ -24,4 +25,20 @@ router.use('/feedback', feedbackRoute); router.use('/notification', notificationRoute); router.use('/chat', chatBot); -export default router; +// ROUTES FOR TESTING PURPOSE +router.get('/test', (req: Request, res: Response) => { + res.status(200).json({ message: 'Route works!' }); +}); +router.post('/test/posting', (req: Request, res: Response) =>{ + return responseSuccess(res, 200, req.body); +}); + +router.get('/test/secure', authMiddleware as RequestHandler, (req: Request, res: Response) =>{ + responseSuccess(res, 200, 'This is a secured route.'); +}); + +router.get('/test/error', (req: Request, res: Response) => { + responseServerError(res, 'This is server error route.'); +}); + +export default router; \ No newline at end of file diff --git a/src/services/adminOrderServices/updateOrder.ts b/src/services/adminOrderServices/updateOrder.ts index b6cdb2e..876160f 100644 --- a/src/services/adminOrderServices/updateOrder.ts +++ b/src/services/adminOrderServices/updateOrder.ts @@ -2,11 +2,9 @@ import { Request, Response } from 'express'; import { Not, getRepository } from 'typeorm'; import { responseSuccess, responseError } from '../../utils/response.utils'; import { VendorOrderItem } from '../../entities/VendorOrderItem'; -import { sendNotification } from '../../utils/sendNotification'; import { VendorOrders } from '../../entities/vendorOrders'; import { Order } from '../../entities/Order'; import { getIO } from '../../utils/socket'; -import { getNotifications } from '../../utils/getNotifications'; export const updateBuyerVendorOrderService = async (req: Request, res: Response) => { try { @@ -40,7 +38,7 @@ export const updateBuyerVendorOrderService = async (req: Request, res: Response) id: order.id, }, }, - relations: ['vendor','order.buyer','vendorOrderItems', 'vendorOrderItems.product'], + relations: ['vendor', 'vendorOrderItems', 'vendorOrderItems.product'], }); for (const order of vendorOrders) { @@ -54,23 +52,9 @@ export const updateBuyerVendorOrderService = async (req: Request, res: Response) order.orderStatus = 'completed'; await orderRepository.save(order); - await sendNotification({ - content: 'Your order was marked completed', - type: 'order', - user: order.buyer, - link: `/product/client/orders/${order.id}` - }); - const updatedVendorOrder = vendorOrders.map(async order => { order.orderStatus = 'completed'; await vendorOrderRepository.save(order); - - await sendNotification({ - content:`Order from buyer "${order.order.buyer.firstName} ${order.order.buyer.lastName}" has been marked completed`, - type: 'order', - user: order.vendor, - link: `/product/vendor/orders/${order.id}` - }); }); const sanitizedOrderResponse = { diff --git a/src/services/cartServices/clearCart.ts b/src/services/cartServices/clearCart.ts index 4806e01..1f24572 100644 --- a/src/services/cartServices/clearCart.ts +++ b/src/services/cartServices/clearCart.ts @@ -44,6 +44,8 @@ export const clearCartService = async (req: Request, res: Response) => { }); if (!cart) { + console.log(cart); + responseSuccess(res, 200, 'Cart is empty', { cart: [] }); return; } diff --git a/src/services/cartServices/readCart.ts b/src/services/cartServices/readCart.ts index 71d7a4d..a891768 100644 --- a/src/services/cartServices/readCart.ts +++ b/src/services/cartServices/readCart.ts @@ -55,4 +55,4 @@ export const readCartService = async (req: Request, res: Response) => { responseError(res, 400, (error as Error).message); return; } -}; +}; \ No newline at end of file diff --git a/src/services/cartServices/removeProductInCart.ts b/src/services/cartServices/removeProductInCart.ts index 25bfe13..b8894d4 100644 --- a/src/services/cartServices/removeProductInCart.ts +++ b/src/services/cartServices/removeProductInCart.ts @@ -9,25 +9,20 @@ export const removeProductInCartService = async (req: Request, res: Response) => try { const cartItemRepository = getRepository(CartItem); const cartRepository = getRepository(Cart); + + if (req.user) { + const cartItem = await cartItemRepository.findOne({ + where: { + id: req.params.id, + }, + relations: ['cart', 'cart.user'], + }); - if (!req.params.id) { - responseError(res, 400, 'Cart item id is required'); - return; - } - - const cartItem = await cartItemRepository.findOne({ - where: { - id: req.params.id, - }, - relations: ['cart', 'cart.user'], - }); - - if (!cartItem) { - responseError(res, 404, 'Cart item not found'); - return; - } + if (!cartItem) { + responseError(res, 404, 'Cart item not found'); + return; + } - if (req.user) { if (cartItem?.cart.user.id !== req.user.id) { responseError(res, 401, 'You are not authorized to perform this action'); return; @@ -61,11 +56,6 @@ export const removeProductInCartService = async (req: Request, res: Response) => } if (!req.user) { - if (!req.params.id) { - responseError(res, 400, 'Cart item id is required'); - return; - } - const cartItem = await cartItemRepository.findOne({ where: { id: req.params.id, @@ -89,12 +79,12 @@ export const removeProductInCartService = async (req: Request, res: Response) => if (cart) { if (cart.items.length === 0) { + await cartRepository.remove(cart); responseSuccess(res, 200, 'cart removed successfully', { cart: [] }); return; } - cart.updateTotal(); await cartRepository.save(cart); diff --git a/src/services/couponServices/accessAllCoupon.ts b/src/services/couponServices/accessAllCoupon.ts index 9266a44..9f9aacf 100644 --- a/src/services/couponServices/accessAllCoupon.ts +++ b/src/services/couponServices/accessAllCoupon.ts @@ -13,7 +13,6 @@ export const accessAllCouponService = async (req: Request, res: Response) => { const user = await userRepository.findOne({ where: { id } }); if (!user) { - console.log('User not found with id:', id); return responseError(res, 404, 'User not found'); } @@ -25,13 +24,11 @@ export const accessAllCouponService = async (req: Request, res: Response) => { }); if (!coupons.length) { - console.log('No coupons found for user with id:', id); return responseError(res, 404, 'No coupons found'); } return responseSuccess(res, 200, 'Coupons retrieved successfully', coupons); } catch (error: any) { - console.log('Error retrieving all coupons:\n', error); return responseServerError(res, error); } }; diff --git a/src/services/couponServices/createCouponService.ts b/src/services/couponServices/createCouponService.ts index 592e462..301e2ea 100644 --- a/src/services/couponServices/createCouponService.ts +++ b/src/services/couponServices/createCouponService.ts @@ -10,6 +10,7 @@ export const createCouponService = async (req: Request, res: Response) => { try { const { error } = validateCoupon(req.body); if (error) { + console.log('Validation Error creating coupon:\n', error); return res.status(400).json({ status: 'error', error: error?.details[0].message }); } @@ -19,14 +20,12 @@ export const createCouponService = async (req: Request, res: Response) => { const userRepository = getRepository(User); const user = await userRepository.findOne({ where: { id: vendorId } }); if (!user) { - console.log('Error creating coupon: User not found', user); return responseError(res, 404, 'User not found'); } const productRepository = getRepository(Product); const product = await productRepository.findOne({ where: { id: productId } }); if (!product) { - console.log('Error creating coupon: Product not found', product); return responseError(res, 403, 'Product not found'); } @@ -48,7 +47,6 @@ export const createCouponService = async (req: Request, res: Response) => { await couponRepository.save(newCoupon); responseSuccess(res, 201, 'Coupon created successfully'); } catch (error: any) { - console.log('Error creating coupon:\n', error); return responseServerError(res, error); } }; diff --git a/src/services/couponServices/deleteCoupon.ts b/src/services/couponServices/deleteCoupon.ts index c984d9e..ac253e6 100644 --- a/src/services/couponServices/deleteCoupon.ts +++ b/src/services/couponServices/deleteCoupon.ts @@ -9,7 +9,6 @@ export const deleteCouponService = async (req: Request, res: Response) => { const coupon = await couponRepository.findOne({ where: { code: req.body.code } }); if (!coupon) { - console.log('Invalid coupon.'); return responseError(res, 404, 'Invalid coupon'); } @@ -17,7 +16,6 @@ export const deleteCouponService = async (req: Request, res: Response) => { return responseSuccess(res, 200, 'Coupon deleted successfully'); } catch (error: any) { - console.log('Error deleting coupon:\n', error); return responseServerError(res, error); } }; diff --git a/src/services/couponServices/readCoupon.ts b/src/services/couponServices/readCoupon.ts index 47e12ea..5af35df 100644 --- a/src/services/couponServices/readCoupon.ts +++ b/src/services/couponServices/readCoupon.ts @@ -17,7 +17,6 @@ export const readCouponService = async (req: Request, res: Response) => { return responseSuccess(res, 200, 'Coupon retrieved successfully', coupon); } catch (error: any) { - console.log('Error retrieving coupon:\n', error); return responseServerError(res, error); } }; diff --git a/src/services/couponServices/updateService.ts b/src/services/couponServices/updateService.ts index 26aeef6..d1d0eab 100644 --- a/src/services/couponServices/updateService.ts +++ b/src/services/couponServices/updateService.ts @@ -17,8 +17,8 @@ export const updateCouponService = async (req: Request, res: Response) => { const coupon = await couponRepository.findOne({ where: { code } }); if (coupon) { if (req.body.code !== undefined) { - const existtCoupon = await couponRepository.findOne({ where: { code: req.body.code } }); - if (existtCoupon) return responseError(res, 400, 'Coupon code already exists'); + const existCoupon = await couponRepository.findOne({ where: { code: req.body.code } }); + if (existCoupon) return responseError(res, 400, 'Coupon code already exists'); if (req.body.code === coupon.code) return responseError(res, 400, 'Coupon code already up to date'); coupon.code = req.body.code; } @@ -35,11 +35,12 @@ export const updateCouponService = async (req: Request, res: Response) => { coupon.discountType = req.body.discountType; } if (req.body.product !== undefined) { - const { id } = req.body.product; + const id = req.body.product; + const productRepository = getRepository(Product); const product = await productRepository.findOne({ where: { id } }); + if (!product) { - console.log('Error updating coupon: Product not found', product); return responseError(res, 404, 'Product not found'); } @@ -49,11 +50,9 @@ export const updateCouponService = async (req: Request, res: Response) => { await couponRepository.save(coupon); return responseSuccess(res, 200, 'Coupon updated successfully', coupon); } else { - console.log('Error updating coupon: Coupon not found', coupon); return responseError(res, 404, 'Coupon not found'); } } catch (error: any) { - console.log('Error while updating coupon:\n', error); return responseServerError(res, error); } -}; +}; \ No newline at end of file diff --git a/src/services/feedbackServices/adminDeleteFeedback.ts b/src/services/feedbackServices/adminDeleteFeedback.ts index 7bf6261..287b87c 100644 --- a/src/services/feedbackServices/adminDeleteFeedback.ts +++ b/src/services/feedbackServices/adminDeleteFeedback.ts @@ -18,7 +18,7 @@ export const adminDeleteFeedbackService = async (req: Request, res: Response) => await feedbackRepository.remove(feedback); - return responseSuccess(res, 200, 'Feedback successfully removed'); + return responseSuccess(res, 200, 'Feedback successfully removed'); } catch (error) { return responseError(res, 500, 'Server error'); } diff --git a/src/services/notificationServices/updateNotification.ts b/src/services/notificationServices/updateNotification.ts index c8295d9..4260789 100644 --- a/src/services/notificationServices/updateNotification.ts +++ b/src/services/notificationServices/updateNotification.ts @@ -82,20 +82,31 @@ export const updateAllNotificationsService = async (req: Request, res: Response) isRead: false } }, - relations: ['allNotifications'] + relations: { + allNotifications: true + } }); - + if (!notification || !notification.allNotifications.length) { responseSuccess(res, 200, "User doesn't have any unread notifications."); return; } - for (const notificationItem of notification.allNotifications) { + const notificationItems = await notificationItemRepo.find({ + where: { + notification: { + id: notification.id + } + } + }); + + for (const notificationItem of notificationItems) { notificationItem.isRead = true; await notificationItemRepo.save(notificationItem); } if (notification) { + notification.allNotifications = notificationItems; notification.updateUnread(); await notificationRepo.save(notification); } diff --git a/src/services/orderServices/getOrderService.ts b/src/services/orderServices/getOrderService.ts index 4006478..17a29a8 100644 --- a/src/services/orderServices/getOrderService.ts +++ b/src/services/orderServices/getOrderService.ts @@ -116,4 +116,4 @@ export const getOrderService = async (req: Request, res: Response) => { } catch (error) { return responseError(res, 400, (error as Error).message); } -}; +}; \ No newline at end of file diff --git a/src/services/orderServices/updateOrderService.ts b/src/services/orderServices/updateOrderService.ts index d10f5d3..23b7662 100644 --- a/src/services/orderServices/updateOrderService.ts +++ b/src/services/orderServices/updateOrderService.ts @@ -32,9 +32,6 @@ export const updateOrderService = async (req: Request, res: Response) => { const transactionRepository: Repository = transactionalEntityManager.getRepository(Transaction); const buyerId = req.user?.id; - if (!buyerId) { - throw new Error('Unauthorized'); - } // Fetch order and related entities const order: Order | null = await orderRepository.findOne({ @@ -132,7 +129,6 @@ export const updateOrderService = async (req: Request, res: Response) => { return sendSuccessResponse(res, 200, 'Order updated successfully', orderResponse); }); } catch (error) { - console.error('Error updating order:', (error as Error).message); return sendErrorResponse(res, 500, (error as Error).message); } }; @@ -167,4 +163,4 @@ async function processRefund (order: Order, entityManager: EntityManager) { function isOrderFinalStatus (status: string): boolean { return ['cancelled', 'delivered', 'returned', 'completed'].includes(status); -} +} \ No newline at end of file diff --git a/src/services/productServices/createProduct.ts b/src/services/productServices/createProduct.ts index 668ddd2..44918d8 100644 --- a/src/services/productServices/createProduct.ts +++ b/src/services/productServices/createProduct.ts @@ -101,4 +101,4 @@ export const createProductService = async (req: Request, res: Response) => { } catch (error) { res.status(400).json({ message: (error as Error).message }); } -}; +}; \ No newline at end of file diff --git a/src/services/productServices/listAllProductsService.ts b/src/services/productServices/listAllProductsService.ts index 4429e89..e9fa0ee 100644 --- a/src/services/productServices/listAllProductsService.ts +++ b/src/services/productServices/listAllProductsService.ts @@ -33,6 +33,9 @@ export const listAllProductsService = async (req: Request, res: Response) => { }, }); + if (products.length < 1) { + return responseSuccess(res, 200, 'No products found'); + } if (products.length < 1) { return responseSuccess(res, 200, 'No products found'); } @@ -41,4 +44,4 @@ export const listAllProductsService = async (req: Request, res: Response) => { } catch (error) { responseError(res, 400, (error as Error).message); } -}; +}; \ No newline at end of file diff --git a/src/services/productServices/productStatus.ts b/src/services/productServices/productStatus.ts index 16509c3..708621b 100644 --- a/src/services/productServices/productStatus.ts +++ b/src/services/productServices/productStatus.ts @@ -7,12 +7,11 @@ import { responseSuccess, responseError, responseServerError } from '../../utils export const productStatusServices = async (req: Request, res: Response) => { try { const { isAvailable } = req.body; - const availability = isAvailable; const { id } = req.params; - if (availability === undefined) { + if (isAvailable === undefined) { console.log('Error: Please fill all the required fields'); - return responseError(res, 401, 'Please fill all t he required fields'); + return responseError(res, 400, 'Please fill all t he required fields'); } const userRepository = getRepository(User); @@ -45,15 +44,14 @@ export const productStatusServices = async (req: Request, res: Response) => { if (hasProduct.expirationDate && hasProduct.expirationDate < new Date()) { hasProduct.isAvailable = false; await productRepository.save(hasProduct); - return responseSuccess(res, 201, 'Product status is set to false because it is expired.'); + return responseSuccess(res, 200, 'Product status is set to false because it is expired.'); } else if (hasProduct.quantity < 1) { product.isAvailable = false; await productRepository.save(hasProduct); - return responseSuccess(res, 202, 'Product status is set to false because it is out of stock.'); + return responseSuccess(res, 200, 'Product status is set to false because it is out of stock.'); } if (hasProduct.isAvailable === isAvailable) { - console.log('Error: Product status is already updated'); responseError(res, 400, 'Product status is already up to date'); return; } @@ -63,7 +61,6 @@ export const productStatusServices = async (req: Request, res: Response) => { return responseSuccess(res, 200, 'Product status updated successfully'); } catch (error) { - console.log('Error: Product status is not update due to this error:\n', error); return responseServerError(res, 'Sorry, Something went wrong. Try again later.'); } }; diff --git a/src/services/productServices/readProduct.ts b/src/services/productServices/readProduct.ts index 2836b21..77896ce 100644 --- a/src/services/productServices/readProduct.ts +++ b/src/services/productServices/readProduct.ts @@ -75,4 +75,4 @@ export const readProductService = async (req: Request, res: Response) => { } catch (error) { responseError(res, 400, (error as Error).message); } -}; +}; \ No newline at end of file diff --git a/src/services/productServices/searchProduct.ts b/src/services/productServices/searchProduct.ts index 9f33b5f..123672f 100644 --- a/src/services/productServices/searchProduct.ts +++ b/src/services/productServices/searchProduct.ts @@ -1,5 +1,5 @@ import { Request, Response } from 'express'; -import { getRepository, Like } from 'typeorm'; +import { getRepository } from 'typeorm'; import { Product } from '../../entities/Product'; interface SearchProductParams { @@ -10,33 +10,45 @@ interface SearchProductParams { limit?: number; } -export const searchProductService = async (params: SearchProductParams) => { - const { name, sortBy, sortOrder, page = 1, limit = 10 } = params; - - const productRepository = getRepository(Product); - let query = productRepository.createQueryBuilder('product'); - - if (name) { - query = query.where('product.name LIKE :name', { name: `%${name}%` }); - } - - if (sortBy && sortOrder) { - query = query.orderBy(`product.${sortBy}`, sortOrder as 'ASC' | 'DESC'); +export const searchProductService = async (req: Request, res: Response) => { + const { name, sortBy, sortOrder, page = 1, limit = 10 }: SearchProductParams = req.query as any; + try { + if (!name) { + console.log("no name"); + return res.status(400).json({ status: 'error', error: 'Please provide a search term' }); + } + + const productRepository = getRepository(Product); + let query = productRepository.createQueryBuilder('product'); + + query = query.where('LOWER(product.name) LIKE :name', { name: `%${name.toLowerCase()}%` }); + + if (sortBy && sortOrder) { + query = query.orderBy(`product.${sortBy}`, sortOrder as 'ASC' | 'DESC'); + } + + const skip = (page - 1) * limit; + + const [products, total] = await query.skip(skip).take(limit).getManyAndCount(); + + if (total === 0) { + return res.status(404).json({ status: 'error', error: 'No products found' }); + } + + const totalPages = Math.ceil(total / limit); + + return res.status(200).json({ + status: 'success', + data: products, + pagination: { + totalItems: total, + currentPage: page, + totalPages, + itemsPerPage: limit, + }, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ status: 'error', error: 'Something went wrong' }); } - - const skip = (page - 1) * limit; - - const [products, total] = await query.skip(skip).take(limit).getManyAndCount(); - - const totalPages = Math.ceil(total / limit); - - return { - data: products, - pagination: { - totalItems: total, - currentPage: page, - totalPages, - itemsPerPage: limit, - }, - }; -}; +}; \ No newline at end of file diff --git a/src/services/productServices/viewSingleProduct.ts b/src/services/productServices/viewSingleProduct.ts index be9764d..6f49532 100644 --- a/src/services/productServices/viewSingleProduct.ts +++ b/src/services/productServices/viewSingleProduct.ts @@ -28,4 +28,4 @@ export const viewSingleProduct = async (req: Request, res: Response) => { console.error('Error handling request:', error); res.status(500).send('Error fetching product details'); } -}; +}; \ No newline at end of file diff --git a/src/services/userServices/sendResetPasswordLinkService.ts b/src/services/userServices/sendResetPasswordLinkService.ts index f9b7dbf..1d34666 100644 --- a/src/services/userServices/sendResetPasswordLinkService.ts +++ b/src/services/userServices/sendResetPasswordLinkService.ts @@ -5,30 +5,30 @@ import { getRepository } from 'typeorm'; import { User } from '../../entities/User'; export const sendPasswordResetLinkService = async (req: Request, res: Response) => { - try { - const transporter = nodemailer.createTransport({ - host: process.env.HOST, - port: 587, - secure: false, // true for 465, false for other ports - auth: { - user: process.env.AUTH_EMAIL, - pass: process.env.AUTH_PASSWORD, - }, - }); - const email = req.query.email as string; + try { + const transporter = nodemailer.createTransport({ + host: process.env.HOST, + port: 587, + secure: false, // true for 465, false for other ports + auth: { + user: process.env.AUTH_EMAIL, + pass: process.env.AUTH_PASSWORD, + }, + }); + const email = req.query.email as string; - if (!email) { - return responseError(res, 404, 'Missing required field'); - } - const userRepository = getRepository(User); - const existingUser = await userRepository.findOneBy({ email }); - if (!existingUser) { - return responseError(res, 404, 'User not found', existingUser); - } - const mailOptions: nodemailer.SendMailOptions = { - to: email, - subject: `Password reset link `, - html: ` + if (!email) { + return responseError(res, 400, 'Missing required field'); + } + const userRepository = getRepository(User); + const existingUser = await userRepository.findOneBy({ email }); + if (!existingUser) { + return responseError(res, 404, 'User not found', existingUser); + } + const mailOptions: nodemailer.SendMailOptions = { + to: email, + subject: `Password reset link `, + html: ` @@ -103,15 +103,15 @@ export const sendPasswordResetLinkService = async (req: Request, res: Response) `, - }; + }; - try { - const sendMail = await transporter.sendMail(mailOptions); - return responseSuccess(res, 200, 'Code sent on your email', sendMail); + try { + const sendMail = await transporter.sendMail(mailOptions); + return responseSuccess(res, 200, 'Code sent on your email', sendMail); + } catch (error) { + return responseError(res, 500, 'Error occurred while sending email'); + } } catch (error) { - return responseError(res, 500, 'Error occurred while sending email'); + return responseServerError(res, `Internal server error: `); } - } catch (error) { - return responseServerError(res, `Internal server error: `); - } }; diff --git a/src/services/userServices/userPasswordResetService.ts b/src/services/userServices/userPasswordResetService.ts index 8428f1a..93de01e 100644 --- a/src/services/userServices/userPasswordResetService.ts +++ b/src/services/userServices/userPasswordResetService.ts @@ -12,7 +12,7 @@ export const userPasswordResetService = async (req: Request, res: Response) => { const userId: any = userid; const userRepository = getRepository(User); if (!email || !userid) { - return responseError(res, 404, `Something went wrong while fetching your data`); + return responseError(res, 400, `Something went wrong while fetching your data`); } const existingUser = await userRepository.findOneBy({ email: mail, id: userId }); if (!existingUser) { @@ -20,19 +20,18 @@ export const userPasswordResetService = async (req: Request, res: Response) => { } if (!newPassword || !confirmPassword) { - return responseError(res, 200, 'Please provide all required fields'); + return responseError(res, 400, 'Please provide all required fields'); } if (newPassword !== confirmPassword) { - return responseError(res, 200, 'new password must match confirm password'); + return responseError(res, 400, 'new password must match confirm password'); } const saltRounds = 10; const hashedPassword = await bcrypt.hash(newPassword, saltRounds); existingUser.password = hashedPassword; const updadeUser = await userRepository.save(existingUser); - return responseSuccess(res, 201, 'Password updated successfully', updadeUser); + return responseSuccess(res, 200, 'Password updated successfully', updadeUser); } catch (error) { - console.log('error: reseting password in password reset service'); return responseServerError(res, 'Internal server error'); } }; diff --git a/src/services/userServices/userProfileUpdateServices.ts b/src/services/userServices/userProfileUpdateServices.ts index c140e38..82fb71d 100644 --- a/src/services/userServices/userProfileUpdateServices.ts +++ b/src/services/userServices/userProfileUpdateServices.ts @@ -4,44 +4,34 @@ import { User, UserInterface } from '../../entities/User'; import { getRepository } from 'typeorm'; import { userProfileUpdate } from '../../controllers/authController'; -declare module 'express' { - interface Request { - user?: Partial; - } -} export const userProfileUpdateServices = async (req: Request, res: Response) => { try { - if (!req.body) { - return responseError(res, 401, 'body required'); + + if (Object.keys(req.body).length === 0) { + return responseError(res, 400, 'body required'); } - const { firstName, lastName, gender, phoneNumber, photoUrl, email, id } = req.body; + const { firstName, lastName, gender, phoneNumber, photoUrl} = req.body; // Validate user input if ( - !firstName.trim() && - !lastName.trim() && - !gender.trim() && - !phoneNumber.trim() && - !photoUrl.trim() && - !email.trim() && - !id.trim() + !firstName || !lastName || !gender || + !phoneNumber || !photoUrl ) { return responseError(res, 400, 'Fill all the field'); } const userRepository = getRepository(User); const existingUser = await userRepository.findOne({ - where: { email: req.body.email }, + where: { + id: req.user?.id + }, }); if (!existingUser) { return responseError(res, 401, 'User not found'); } - if (existingUser.id !== id) { - return responseError(res, 403, 'You are not authorized to edit this profile.'); - } existingUser.firstName = firstName; existingUser.lastName = lastName; @@ -50,7 +40,7 @@ export const userProfileUpdateServices = async (req: Request, res: Response) => existingUser.photoUrl = photoUrl; await userRepository.save(existingUser); - return responseSuccess(res, 201, 'User Profile has successfully been updated'); + return responseSuccess(res, 200, 'User Profile has successfully been updated'); } catch (error) { responseError(res, 400, (error as Error).message); } diff --git a/src/services/userServices/userValidationService.ts b/src/services/userServices/userValidationService.ts index a28ff4e..be21726 100644 --- a/src/services/userServices/userValidationService.ts +++ b/src/services/userServices/userValidationService.ts @@ -1,25 +1,30 @@ import { Request, Response } from 'express'; import { User } from '../../entities/User'; import { getRepository } from 'typeorm'; +import { responseError } from '../../utils/response.utils'; export const userVerificationService = async (req: Request, res: Response) => { - const { id } = req.params; + try { + const { id } = req.params; - // Validate user input - if (!id) { - return res.status(400).json({ error: 'Missing user ID' }); - } + // Validate user input + if (!id) { + return res.status(400).json({ error: 'Missing user ID' }); + } - const userRepository = getRepository(User); - const user = await userRepository.findOneBy({ id }); + const userRepository = getRepository(User); + const user = await userRepository.findOneBy({ id }); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } - user.verified = true; + user.verified = true; - await userRepository.save(user); + await userRepository.save(user); - return res.status(200).send('

User verified successfully

'); + return res.status(200).send('

User verified successfully

'); + } catch (error) { + responseError(res, 400, (error as Error).message); + } }; diff --git a/src/services/wishListServices/addProduct.ts b/src/services/wishListServices/addProduct.ts index 79d0a38..4efb80d 100644 --- a/src/services/wishListServices/addProduct.ts +++ b/src/services/wishListServices/addProduct.ts @@ -54,4 +54,4 @@ export const addProductService = async (req: Request, res: Response) => { } catch (error) { return res.status(500).json({ error: 'Internal server error' }); } -}; +}; \ No newline at end of file diff --git a/src/services/wishListServices/clearAll.ts b/src/services/wishListServices/clearAll.ts index 7299454..81f3fc7 100644 --- a/src/services/wishListServices/clearAll.ts +++ b/src/services/wishListServices/clearAll.ts @@ -16,4 +16,4 @@ export const clearAllProductService = async (req: Request, res: Response) => { } catch (error) { return res.status(500).json({ error: 'Internal server error' }); } -}; +}; \ No newline at end of file diff --git a/src/services/wishListServices/getProducts.ts b/src/services/wishListServices/getProducts.ts index 98dc434..a419134 100644 --- a/src/services/wishListServices/getProducts.ts +++ b/src/services/wishListServices/getProducts.ts @@ -36,4 +36,4 @@ export const getProductsService = async (req: Request, res: Response) => { } catch (error) { return res.status(500).json({ error: 'Internal server error' }); } -}; +}; \ No newline at end of file diff --git a/src/services/wishListServices/removeProducts.ts b/src/services/wishListServices/removeProducts.ts index b42052f..f25a837 100644 --- a/src/services/wishListServices/removeProducts.ts +++ b/src/services/wishListServices/removeProducts.ts @@ -18,4 +18,4 @@ export const removeProductService = async (req: Request, res: Response) => { } catch (error) { return res.status(500).json({ error: 'Internal server error' }); } -}; +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 7e48aed..b56f8f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -105,4 +105,4 @@ }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] -} +} \ No newline at end of file