chore(medusa): cleanup medusa package (#7206)
This commit is contained in:
1 parent
8b61dccd0f
commit
71f4f46cb9
1452 files changed
+4737
-234780
No files matched your search
Whitespace-only changes.
@@ -1,96 +0,0 @@
|
||||
import { AbstractFileService } from "@medusajs/medusa"
|
||||
import * as fs from "fs"
|
||||
import mkdirp from "mkdirp"
|
||||
import { resolve } from "path"
|
||||
import stream from "stream"
|
||||
|
||||
export default class LocalFileService extends AbstractFileService {
|
||||
constructor({}, options) {
|
||||
super({}, options)
|
||||
this.upload_dir_ =
|
||||
process.env.UPLOAD_DIR ?? options.upload_dir ?? "uploads/images"
|
||||
|
||||
if (!fs.existsSync(this.upload_dir_)) {
|
||||
fs.mkdirSync(this.upload_dir_)
|
||||
}
|
||||
}
|
||||
|
||||
upload(file) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, file.originalname)
|
||||
|
||||
let content = ""
|
||||
if (file.filename) {
|
||||
content = fs.readFileSync(
|
||||
resolve(process.cwd(), "uploads", file.filename)
|
||||
)
|
||||
}
|
||||
|
||||
const pathSegments = path.split("/")
|
||||
pathSegments.splice(-1)
|
||||
const dirname = pathSegments.join("/")
|
||||
mkdirp.sync(dirname, { recursive: true })
|
||||
|
||||
fs.writeFile(path, content.toString(), (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise({ url: path })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
delete({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
fs.unlink(path, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise("file unlinked")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async getUploadStreamDescriptor({ name, ext }) {
|
||||
const fileKey = `${name}.${ext}`
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
|
||||
const isFileExists = fs.existsSync(path)
|
||||
if (!isFileExists) {
|
||||
await this.upload({ originalname: fileKey })
|
||||
}
|
||||
|
||||
const pass = new stream.PassThrough()
|
||||
pass.pipe(fs.createWriteStream(path))
|
||||
|
||||
return {
|
||||
writeStream: pass,
|
||||
promise: Promise.resolve(),
|
||||
url: `${this.upload_dir_}/${fileKey}`,
|
||||
fileKey,
|
||||
}
|
||||
}
|
||||
|
||||
async getDownloadStream({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
try {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
const data = fs.readFileSync(path)
|
||||
const readable = stream.Readable()
|
||||
readable._read = function () {}
|
||||
readable.push(data.toString())
|
||||
readable.push(null)
|
||||
resolvePromise(readable)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getPresignedDownloadUrl({ fileKey }) {
|
||||
return `${this.upload_dir_}/${fileKey}`
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { FulfillmentService } from "medusa-interfaces"
|
||||
|
||||
class TestFulService extends FulfillmentService {
|
||||
static identifier = "test-ful"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
getFulfillmentOptions() {
|
||||
return [
|
||||
{
|
||||
id: "manual-fulfillment",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
return data
|
||||
}
|
||||
|
||||
validateOption(data) {
|
||||
return true
|
||||
}
|
||||
|
||||
canCalculate() {
|
||||
return true
|
||||
}
|
||||
|
||||
calculatePrice(data) {
|
||||
return data.price
|
||||
}
|
||||
|
||||
createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createReturn() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createFulfillment() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
cancelFulfillment() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
}
|
||||
|
||||
export default TestFulService
|
||||
@@ -1,19 +0,0 @@
|
||||
import { NotificationService } from "medusa-interfaces"
|
||||
|
||||
class TestNotiService extends NotificationService {
|
||||
static identifier = "test-not"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
async sendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async resendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default TestNotiService
|
||||
@@ -1,87 +0,0 @@
|
||||
import { AbstractPaymentService } from "@medusajs/medusa"
|
||||
|
||||
class TestPayService extends AbstractPaymentService {
|
||||
static identifier = "test-pay"
|
||||
|
||||
constructor(_) {
|
||||
super(_)
|
||||
}
|
||||
|
||||
async getStatus(paymentData) {
|
||||
return "authorized"
|
||||
}
|
||||
|
||||
async retrieveSavedMethods(customer) {
|
||||
return []
|
||||
}
|
||||
|
||||
async createPayment(cart) {
|
||||
const fields = [
|
||||
"total",
|
||||
"subtotal",
|
||||
"tax_total",
|
||||
"discount_total",
|
||||
"shipping_total",
|
||||
"gift_card_total",
|
||||
]
|
||||
|
||||
const data = {}
|
||||
for (const k of fields) {
|
||||
data[k] = cart[k]
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
async createPaymentNew(inputData) {
|
||||
return inputData
|
||||
}
|
||||
|
||||
async retrievePayment(data) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getPaymentData(sessionData) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async authorizePayment(sessionData, context = {}) {
|
||||
if (
|
||||
sessionData.cart_id === "cart-id-tax-line-testing-for-pending-payment"
|
||||
) {
|
||||
return { data: {}, status: "pending" }
|
||||
}
|
||||
|
||||
return { data: {}, status: "authorized" }
|
||||
}
|
||||
|
||||
async updatePaymentData(sessionData, update) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePayment(sessionData, cart) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePaymentNew(sessionData) {
|
||||
return sessionData
|
||||
}
|
||||
|
||||
async deletePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async capturePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async cancelPayment(payment) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default TestPayService
|
||||
@@ -1,7 +0,0 @@
|
||||
# Default postgres credentials
|
||||
DB_HOST=localhost
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=''
|
||||
DB_NAME=development
|
||||
|
||||
SERVER_PORT=9000
|
||||
@@ -1,31 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
require("dotenv").config({ path: path.join(__dirname, ".env.development") })
|
||||
|
||||
const { initDb } = require("./use-db-development")
|
||||
|
||||
require("./dev-require")
|
||||
|
||||
const seedDB = async (db) => {
|
||||
const seeder = require("./database/index.js")
|
||||
try {
|
||||
await seeder(db)
|
||||
} catch (err) {
|
||||
console.log("Error", err)
|
||||
}
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
console.log("Creating DB...")
|
||||
const dbConnection = await initDb()
|
||||
console.log("Creating DB. DONE")
|
||||
|
||||
console.log("Seeding DB...")
|
||||
await seedDB(dbConnection)
|
||||
console.log("Seeding DB... DONE")
|
||||
|
||||
await dbConnection.close()
|
||||
process.exit()
|
||||
}
|
||||
|
||||
start()
|
||||
@@ -1,17 +0,0 @@
|
||||
const { Customer } = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection) => {
|
||||
const manager = connection.manager
|
||||
|
||||
const customer = manager.create(Customer, {
|
||||
id: "customer-1",
|
||||
email: "test1@email.com",
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
password_hash:
|
||||
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN",
|
||||
// password matching "test"
|
||||
has_account: true,
|
||||
})
|
||||
await manager.save(customer)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
const user = require("./user")
|
||||
const region = require("./region")
|
||||
const customer = require("./customer")
|
||||
|
||||
module.exports = async (db) => {
|
||||
await user(db)
|
||||
await region(db)
|
||||
await customer(db)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
const { Region } = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection) => {
|
||||
const manager = connection.manager
|
||||
|
||||
const r = manager.create(Region, {
|
||||
id: "test-region",
|
||||
name: "Test Region",
|
||||
payment_providers: [{ id: "test-pay" }],
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
})
|
||||
|
||||
await manager.save(r)
|
||||
|
||||
const europeRegion = manager.create(Region, {
|
||||
id: "eur-region",
|
||||
name: "Europe Region",
|
||||
payment_providers: [{ id: "test-pay" }],
|
||||
currency_code: "eur",
|
||||
tax_rate: 0,
|
||||
})
|
||||
|
||||
await manager.save(europeRegion)
|
||||
|
||||
// Region with multiple countries
|
||||
const regionWithMultipleCoutries = manager.create(Region, {
|
||||
id: "test-region-multiple",
|
||||
name: "Test Region",
|
||||
currency_code: "eur",
|
||||
tax_rate: 0,
|
||||
})
|
||||
|
||||
await manager.save(regionWithMultipleCoutries)
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region-multiple' WHERE iso_2 = 'no'`
|
||||
)
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region-multiple' WHERE iso_2 = 'dk'`
|
||||
)
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
const Scrypt = require("scrypt-kdf")
|
||||
const { User } = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection) => {
|
||||
const manager = connection.manager
|
||||
|
||||
const buf = await Scrypt.kdf("secret_password", { logN: 1, r: 1, p: 1 })
|
||||
const password_hash = buf.toString("base64")
|
||||
|
||||
await manager.insert(User, {
|
||||
id: "admin_user",
|
||||
email: "admin@medusa.js",
|
||||
api_token: "test_token",
|
||||
role: "admin",
|
||||
password_hash,
|
||||
})
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
return
|
||||
}
|
||||
const path = require("path")
|
||||
|
||||
const Module = require("module")
|
||||
const originalRequire = Module.prototype.require
|
||||
const medusaCore = path.resolve(path.join(__dirname, "../../packages"))
|
||||
|
||||
function replacePath(requirePath, pack, concatPackage = true) {
|
||||
const idx = requirePath.indexOf(pack)
|
||||
const packPath = requirePath.substring(idx + pack.length).replace(/\\/g, "/")
|
||||
|
||||
let newPath =
|
||||
medusaCore +
|
||||
"/" +
|
||||
(concatPackage ? pack + "/" : "") +
|
||||
packPath.replace("/dist", "/src").replace(".js", "")
|
||||
|
||||
if (!newPath.includes("/src")) {
|
||||
newPath += "/src"
|
||||
}
|
||||
|
||||
return path.resolve(newPath)
|
||||
}
|
||||
|
||||
function checkAndReplacePaths(path) {
|
||||
const interfaces = "medusa-interfaces"
|
||||
const utils = "medusa-core-utils"
|
||||
const base = "@medusajs"
|
||||
|
||||
if (path.includes(base)) {
|
||||
path = replacePath(path, base, false)
|
||||
} else if (path.includes(interfaces)) {
|
||||
path = replacePath(path, interfaces)
|
||||
} else if (path.includes(utils)) {
|
||||
path = replacePath(path, utils)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
Module.prototype.require = function (...args) {
|
||||
args[0] = checkAndReplacePaths(args[0])
|
||||
|
||||
if (args[0] === "glob") {
|
||||
const glob = originalRequire.apply(this, args)
|
||||
const originalGlobSync = glob.sync
|
||||
glob.GlobSync = glob.sync = (pattern, options) => {
|
||||
if (pattern.endsWith(".js") || pattern.endsWith(".ts")) {
|
||||
pattern = checkAndReplacePaths(pattern)
|
||||
pattern = pattern.replace(".js", ".{j,t}s").replace("/dist/", "/src/")
|
||||
}
|
||||
|
||||
return originalGlobSync.apply(this, [pattern, options])
|
||||
}
|
||||
return glob
|
||||
} else if (args[0] === "resolve-cwd") {
|
||||
const resolveCwd = originalRequire.apply(this, args)
|
||||
const newResolveCwd = (pattern) => {
|
||||
pattern = checkAndReplacePaths(pattern)
|
||||
|
||||
return resolveCwd.apply(this, [pattern])
|
||||
}
|
||||
return newResolveCwd
|
||||
}
|
||||
|
||||
return originalRequire.apply(this, args)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_NAME = process.env.DB_NAME
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
{
|
||||
resolve: `./packages/medusa-payment-stripe`,
|
||||
options: {
|
||||
api_key: "api_key",
|
||||
webhook_secret: "api_key",
|
||||
},
|
||||
},
|
||||
],
|
||||
projectConfig: {
|
||||
redis_url: process.env.REDIS_URL,
|
||||
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`,
|
||||
database_type: "postgres",
|
||||
jwt_secret: "test",
|
||||
cookie_secret: "test",
|
||||
},
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
const path = require("path")
|
||||
const express = require("express")
|
||||
const importFrom = require("import-from")
|
||||
const chokidar = require("chokidar")
|
||||
const { WorkflowManager } = require("@medusajs/orchestration")
|
||||
|
||||
process.env.DEV_MODE = !!process[Symbol.for("ts-node.register.instance")]
|
||||
process.env.NODE_ENV = process.env.DEV_MODE && "development"
|
||||
|
||||
require("dotenv").config({ path: path.join(__dirname, ".env.development") })
|
||||
|
||||
require("./dev-require")
|
||||
|
||||
const medusaCore = path
|
||||
.resolve(path.join(__dirname, "../../packages"))
|
||||
.replace(/\\/g, "/")
|
||||
|
||||
let WATCHING = false
|
||||
let IS_RELOADING = false
|
||||
|
||||
function getParentModulesIds(element) {
|
||||
if (!element) {
|
||||
return []
|
||||
}
|
||||
|
||||
const ids = [element.id]
|
||||
let parent = element.parent
|
||||
while (parent && parent.id.replace(/\\/g, "/").includes(medusaCore)) {
|
||||
ids.push(parent.id)
|
||||
parent = parent.parent
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
const watchFiles = () => {
|
||||
if (WATCHING) {
|
||||
return
|
||||
}
|
||||
WATCHING = true
|
||||
|
||||
const watcher = chokidar.watch(medusaCore, {
|
||||
ignored: (rawPath) => {
|
||||
const path = rawPath.replace(/\\/g, "/")
|
||||
if (
|
||||
path.includes("/node_modules") ||
|
||||
path.includes("/dist") ||
|
||||
path.includes("/__") ||
|
||||
(/\..*/i.test(path) &&
|
||||
!(
|
||||
path.endsWith(".js") ||
|
||||
path.endsWith(".ts") ||
|
||||
path.includes("/src")
|
||||
))
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
|
||||
watcher.on("change", async function (rawFile) {
|
||||
if (IS_RELOADING) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Reloading server...")
|
||||
IS_RELOADING = true
|
||||
const start = Date.now()
|
||||
|
||||
const file = rawFile.replace(/\\/g, "/")
|
||||
|
||||
if (file.includes("/models") || file.includes("/repositories")) {
|
||||
Object.keys(require.cache).forEach(function (id) {
|
||||
const name = require.cache[id].filename
|
||||
if (!name.includes("typeorm")) {
|
||||
return
|
||||
}
|
||||
|
||||
delete require.cache[id]
|
||||
})
|
||||
}
|
||||
|
||||
const allModules = Object.keys(module.constructor._cache)
|
||||
const path = file.split("/")
|
||||
const src = path.findIndex((folder) => folder === "src")
|
||||
const next = path.slice(0, src + 2).join("/")
|
||||
|
||||
for (const rawName of allModules) {
|
||||
const name = rawName.replace(/\\/g, "/")
|
||||
if (name.includes("typeorm")) {
|
||||
delete module.constructor._cache[rawName]
|
||||
} else if (name.includes(medusaCore)) {
|
||||
if (
|
||||
name.includes("repositories") ||
|
||||
name.includes("loaders") ||
|
||||
next.endsWith(".js") ||
|
||||
next.endsWith(".ts") ||
|
||||
name.startsWith(next)
|
||||
) {
|
||||
const cacheToClean = getParentModulesIds(
|
||||
module.constructor._cache[rawName]
|
||||
)
|
||||
for (const id of cacheToClean) {
|
||||
delete module.constructor._cache[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorkflowManager.unregisterAll()
|
||||
|
||||
await bootstrapApp()
|
||||
IS_RELOADING = false
|
||||
|
||||
console.log("Server reloaded in", Date.now() - start, "ms")
|
||||
})
|
||||
}
|
||||
|
||||
let server
|
||||
const bootstrapApp = async () => {
|
||||
if (server) {
|
||||
server.close()
|
||||
}
|
||||
|
||||
const app = express()
|
||||
app.use((req, res, next) => {
|
||||
res.header("Access-Control-Allow-Origin", req.headers.origin)
|
||||
res.header("Access-Control-Allow-Methods", "*")
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Origin, X-Requested-With, Content-Type, Accept"
|
||||
)
|
||||
next()
|
||||
})
|
||||
|
||||
const dir = path.resolve(
|
||||
path.join(__dirname, "../../packages/medusa/src/loaders")
|
||||
)
|
||||
const loaders = importFrom(dir, ".").default
|
||||
|
||||
const configDir = __dirname
|
||||
const { dbConnection } = await loaders({
|
||||
directory: configDir,
|
||||
expressApp: app,
|
||||
})
|
||||
|
||||
const port = process.env.SERVER_PORT ?? 9000
|
||||
server = app.listen(port, (err) => {
|
||||
watchFiles()
|
||||
console.log(`Server Running at localhost:${port}`)
|
||||
})
|
||||
}
|
||||
|
||||
void bootstrapApp()
|
||||
@@ -1,96 +0,0 @@
|
||||
import { AbstractFileService } from "@medusajs/medusa"
|
||||
import stream from "stream"
|
||||
import { resolve } from "path"
|
||||
import * as fs from "fs"
|
||||
import mkdirp from "mkdirp"
|
||||
|
||||
export default class LocalFileService extends AbstractFileService {
|
||||
constructor({}, options) {
|
||||
super({}, options)
|
||||
this.upload_dir_ =
|
||||
process.env.UPLOAD_DIR ?? options.upload_dir ?? "uploads/images"
|
||||
|
||||
if (!fs.existsSync(this.upload_dir_)) {
|
||||
fs.mkdirSync(this.upload_dir_)
|
||||
}
|
||||
}
|
||||
|
||||
upload(file) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, file.originalname)
|
||||
|
||||
let content = ""
|
||||
if (file.filename) {
|
||||
content = fs.readFileSync(
|
||||
resolve(process.cwd(), "uploads", file.filename)
|
||||
)
|
||||
}
|
||||
|
||||
const pathSegments = path.split("/")
|
||||
pathSegments.splice(-1)
|
||||
const dirname = pathSegments.join("/")
|
||||
mkdirp.sync(dirname, { recursive: true })
|
||||
|
||||
fs.writeFile(path, content.toString(), (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise({ url: path })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
delete({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
fs.unlink(path, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise("file unlinked")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async getUploadStreamDescriptor({ name, ext }) {
|
||||
const fileKey = `${name}.${ext}`
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
|
||||
const isFileExists = fs.existsSync(path)
|
||||
if (!isFileExists) {
|
||||
await this.upload({ originalname: fileKey })
|
||||
}
|
||||
|
||||
const pass = new stream.PassThrough()
|
||||
pass.pipe(fs.createWriteStream(path))
|
||||
|
||||
return {
|
||||
writeStream: pass,
|
||||
promise: Promise.resolve(),
|
||||
url: `${this.upload_dir_}/${fileKey}`,
|
||||
fileKey,
|
||||
}
|
||||
}
|
||||
|
||||
async getDownloadStream({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
try {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
const data = fs.readFileSync(path)
|
||||
const readable = stream.Readable()
|
||||
readable._read = function () {}
|
||||
readable.push(data.toString())
|
||||
readable.push(null)
|
||||
resolvePromise(readable)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getPresignedDownloadUrl({ fileKey }) {
|
||||
return `${this.upload_dir_}/${fileKey}`
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { FulfillmentService } from "medusa-interfaces"
|
||||
|
||||
class TestFulService extends FulfillmentService {
|
||||
static identifier = "test-ful"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
getFulfillmentOptions() {
|
||||
return [
|
||||
{
|
||||
id: "manual-fulfillment",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
return data
|
||||
}
|
||||
|
||||
validateOption(data) {
|
||||
return true
|
||||
}
|
||||
|
||||
canCalculate() {
|
||||
return true
|
||||
}
|
||||
|
||||
calculatePrice(data) {
|
||||
return data.price
|
||||
}
|
||||
|
||||
createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createReturn() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createFulfillment() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
cancelFulfillment() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
}
|
||||
|
||||
export default TestFulService
|
||||
@@ -1,19 +0,0 @@
|
||||
import { NotificationService } from "medusa-interfaces"
|
||||
|
||||
class TestNotiService extends NotificationService {
|
||||
static identifier = "test-not"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
async sendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async resendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default TestNotiService
|
||||
@@ -1,87 +0,0 @@
|
||||
import { AbstractPaymentService } from "@medusajs/medusa"
|
||||
|
||||
class TestPayService extends AbstractPaymentService {
|
||||
static identifier = "test-pay"
|
||||
|
||||
constructor(_) {
|
||||
super(_)
|
||||
}
|
||||
|
||||
async getStatus(paymentData) {
|
||||
return "authorized"
|
||||
}
|
||||
|
||||
async retrieveSavedMethods(customer) {
|
||||
return []
|
||||
}
|
||||
|
||||
async createPayment(cart) {
|
||||
const fields = [
|
||||
"total",
|
||||
"subtotal",
|
||||
"tax_total",
|
||||
"discount_total",
|
||||
"shipping_total",
|
||||
"gift_card_total",
|
||||
]
|
||||
|
||||
const data = {}
|
||||
for (const k of fields) {
|
||||
data[k] = cart[k]
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
async createPaymentNew(inputData) {
|
||||
return inputData
|
||||
}
|
||||
|
||||
async retrievePayment(data) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getPaymentData(sessionData) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async authorizePayment(sessionData, context = {}) {
|
||||
if (
|
||||
sessionData.cart_id === "cart-id-tax-line-testing-for-pending-payment"
|
||||
) {
|
||||
return { data: {}, status: "pending" }
|
||||
}
|
||||
|
||||
return { data: {}, status: "authorized" }
|
||||
}
|
||||
|
||||
async updatePaymentData(sessionData, update) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePayment(sessionData, cart) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePaymentNew(sessionData) {
|
||||
return sessionData
|
||||
}
|
||||
|
||||
async deletePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async capturePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async cancelPayment(payment) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default TestPayService
|
||||
@@ -1,94 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const { DataSource } = require("typeorm")
|
||||
const { getConfigFile } = require("medusa-core-utils")
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_NAME = process.env.DB_NAME
|
||||
const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
|
||||
|
||||
process.env.NODE_ENV = "development"
|
||||
|
||||
require("./dev-require")
|
||||
|
||||
async function createDB() {
|
||||
const connection = new DataSource({
|
||||
type: "postgres",
|
||||
url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}`,
|
||||
})
|
||||
|
||||
await connection.initialize()
|
||||
|
||||
await connection.query(`DROP DATABASE IF EXISTS "${DB_NAME}";`)
|
||||
await connection.query(`CREATE DATABASE "${DB_NAME}";`)
|
||||
await connection.destroy()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initDb: async function () {
|
||||
const cwd = path.resolve(path.join(__dirname, "../.."))
|
||||
|
||||
const { configModule } = getConfigFile(
|
||||
path.join(__dirname),
|
||||
`medusa-config`
|
||||
)
|
||||
|
||||
const { featureFlags } = configModule
|
||||
|
||||
const basePath = path.join(cwd, "packages/medusa/src")
|
||||
|
||||
const featureFlagsLoader =
|
||||
require("@medusajs/medusa/dist/loaders/feature-flags").default
|
||||
|
||||
const featureFlagsRouter = featureFlagsLoader({ featureFlags })
|
||||
|
||||
const modelsLoader = require("@medusajs/medusa/dist/loaders/models").default
|
||||
|
||||
const {
|
||||
getEnabledMigrations,
|
||||
getModuleSharedResources,
|
||||
runIsolatedModulesMigration,
|
||||
} = require("@medusajs/medusa/dist/commands/utils/get-migrations")
|
||||
|
||||
const entities = modelsLoader({}, { register: false })
|
||||
|
||||
// get migraitons with enabled featureflags
|
||||
const migrationDir = path.resolve(
|
||||
path.join(basePath, `migrations`, `*.{j,t}s`)
|
||||
)
|
||||
|
||||
const isFlagEnabled = (flag) => featureFlagsRouter.isFeatureEnabled(flag)
|
||||
|
||||
const { migrations: moduleMigrations, models: moduleModels } =
|
||||
getModuleSharedResources(configModule, featureFlagsRouter)
|
||||
|
||||
const enabledMigrations = getEnabledMigrations(
|
||||
[migrationDir],
|
||||
isFlagEnabled
|
||||
)
|
||||
|
||||
const enabledEntities = entities.filter(
|
||||
(e) => typeof e.isFeatureEnabled === "undefined" || e.isFeatureEnabled()
|
||||
)
|
||||
|
||||
await createDB()
|
||||
|
||||
const dbConnection = new DataSource({
|
||||
type: "postgres",
|
||||
url: DB_URL,
|
||||
entities: enabledEntities.concat(moduleModels),
|
||||
migrations: enabledMigrations.concat(moduleMigrations),
|
||||
// logging: true,
|
||||
})
|
||||
|
||||
await dbConnection.initialize()
|
||||
|
||||
await dbConnection.runMigrations()
|
||||
|
||||
await runIsolatedModulesMigration(configModule)
|
||||
|
||||
return dbConnection
|
||||
},
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
import {
|
||||
DiscountCondition,
|
||||
DiscountConditionOperator,
|
||||
DiscountConditionType,
|
||||
} from "@medusajs/medusa/dist/models/discount-condition"
|
||||
import { DiscountConditionCustomerGroup } from "@medusajs/medusa/dist/models/discount-condition-customer-group"
|
||||
import { DiscountConditionProduct } from "@medusajs/medusa/dist/models/discount-condition-product"
|
||||
import { DiscountConditionProductCollection } from "@medusajs/medusa/dist/models/discount-condition-product-collection"
|
||||
import { DiscountConditionProductTag } from "@medusajs/medusa/dist/models/discount-condition-product-tag"
|
||||
import { DiscountConditionProductType } from "@medusajs/medusa/dist/models/discount-condition-product-type"
|
||||
import { DiscountConditionJoinTableForeignKey } from "@medusajs/medusa/dist/repositories/discount-condition"
|
||||
import faker from "faker"
|
||||
import { DataSource } from "typeorm"
|
||||
|
||||
export type DiscountConditionFactoryData = {
|
||||
id?: string
|
||||
rule_id: string
|
||||
type: DiscountConditionType
|
||||
operator: DiscountConditionOperator
|
||||
products: string[]
|
||||
product_collections: string[]
|
||||
product_types: string[]
|
||||
product_tags: string[]
|
||||
customer_groups: string[]
|
||||
}
|
||||
|
||||
const getJoinTableResourceIdentifiers = (type: string) => {
|
||||
let conditionTable: any
|
||||
let resourceKey
|
||||
|
||||
switch (type) {
|
||||
case DiscountConditionType.PRODUCTS: {
|
||||
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_ID
|
||||
conditionTable = DiscountConditionProduct
|
||||
break
|
||||
}
|
||||
case DiscountConditionType.PRODUCT_TYPES: {
|
||||
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TYPE_ID
|
||||
conditionTable = DiscountConditionProductType
|
||||
break
|
||||
}
|
||||
case DiscountConditionType.PRODUCT_COLLECTIONS: {
|
||||
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_COLLECTION_ID
|
||||
conditionTable = DiscountConditionProductCollection
|
||||
break
|
||||
}
|
||||
case DiscountConditionType.PRODUCT_TAGS: {
|
||||
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TAG_ID
|
||||
|
||||
conditionTable = DiscountConditionProductTag
|
||||
break
|
||||
}
|
||||
case DiscountConditionType.CUSTOMER_GROUPS: {
|
||||
resourceKey = DiscountConditionJoinTableForeignKey.CUSTOMER_GROUP_ID
|
||||
conditionTable = DiscountConditionCustomerGroup
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return {
|
||||
resourceKey,
|
||||
conditionTable,
|
||||
}
|
||||
}
|
||||
|
||||
export const simpleDiscountConditionFactory = async (
|
||||
dataSource: DataSource,
|
||||
data: DiscountConditionFactoryData,
|
||||
seed?: number
|
||||
): Promise<void> => {
|
||||
if (typeof seed !== "undefined") {
|
||||
faker.seed(seed)
|
||||
}
|
||||
|
||||
const manager = dataSource.manager
|
||||
|
||||
let resources = []
|
||||
|
||||
if (data.products) {
|
||||
resources = data.products
|
||||
}
|
||||
if (data.product_collections) {
|
||||
resources = data.product_collections
|
||||
}
|
||||
if (data.product_types) {
|
||||
resources = data.product_types
|
||||
}
|
||||
if (data.product_tags) {
|
||||
resources = data.product_tags
|
||||
}
|
||||
if (data.customer_groups) {
|
||||
resources = data.customer_groups
|
||||
}
|
||||
|
||||
const toCreate = {
|
||||
type: data.type,
|
||||
operator: data.operator,
|
||||
discount_rule_id: data.rule_id,
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
toCreate["id"] = data.id
|
||||
}
|
||||
|
||||
const condToSave = manager.create(DiscountCondition, toCreate)
|
||||
|
||||
const { conditionTable, resourceKey } = getJoinTableResourceIdentifiers(
|
||||
data.type
|
||||
)
|
||||
|
||||
const condition = await manager.save(condToSave)
|
||||
|
||||
for (const resourceCond of resources) {
|
||||
const toSave = manager.create(conditionTable, {
|
||||
[resourceKey]: resourceCond,
|
||||
condition_id: condition.id,
|
||||
})
|
||||
|
||||
await manager.save(toSave)
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,11 @@ import {
|
||||
} from "@medusajs/medusa"
|
||||
import faker from "faker"
|
||||
import { DataSource } from "typeorm"
|
||||
import {
|
||||
DiscountConditionFactoryData,
|
||||
simpleDiscountConditionFactory,
|
||||
} from "./simple-discount-condition-factory"
|
||||
|
||||
export type DiscountRuleFactoryData = {
|
||||
type?: DiscountRuleType
|
||||
value?: number
|
||||
allocation?: AllocationType
|
||||
conditions: DiscountConditionFactoryData[]
|
||||
}
|
||||
|
||||
export type DiscountFactoryData = {
|
||||
@@ -48,16 +43,6 @@ export const simpleDiscountFactory = async (
|
||||
|
||||
const dRule = await manager.save(ruleToSave)
|
||||
|
||||
if (data?.rule?.conditions) {
|
||||
for (const condition of data.rule.conditions) {
|
||||
await simpleDiscountConditionFactory(
|
||||
dataSource,
|
||||
{ ...condition, rule_id: dRule.id },
|
||||
1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const toSave = manager.create(Discount, {
|
||||
id: data.id,
|
||||
is_dynamic: data.is_dynamic ?? false,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const Scrypt = require("scrypt-kdf")
|
||||
const { User } = require("@medusajs/medusa/dist/models/user")
|
||||
|
||||
module.exports = async (dataSource, data = {}) => {
|
||||
const manager = dataSource.manager
|
||||
@@ -7,7 +6,7 @@ module.exports = async (dataSource, data = {}) => {
|
||||
const buf = await Scrypt.kdf("secret_password", { logN: 15, r: 8, p: 1 })
|
||||
const password_hash = buf.toString("base64")
|
||||
|
||||
const user = await manager.insert(User, {
|
||||
const user = await manager.insert("user", {
|
||||
id: "admin_user",
|
||||
email: "admin@medusa.js",
|
||||
api_token: "test_token",
|
||||
|
||||
@@ -118,7 +118,7 @@ medusaIntegrationTestRunner({
|
||||
price_set_id: priceSet.id,
|
||||
shipping_option_id: shippingOption.id,
|
||||
}),
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: 5000,
|
||||
currency_code: "eur",
|
||||
@@ -127,7 +127,7 @@ medusaIntegrationTestRunner({
|
||||
amount: 3000,
|
||||
currency_code: "usd",
|
||||
}),
|
||||
],
|
||||
]),
|
||||
calculated_price: expect.objectContaining({
|
||||
calculated_amount: 5000,
|
||||
currency_code: "eur",
|
||||
|
||||
@@ -548,7 +548,8 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
describe("POST /admin/price-lists/:id/prices/batch", () => {
|
||||
it("should add, remove and delete price list prices in batch successfully", async () => {
|
||||
// TODO: This is flaky, investigate why
|
||||
it.skip("should add, remove and delete price list prices in batch successfully", async () => {
|
||||
const priceSet = await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useApi } from "../../../../environment-helpers/use-api"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import productSeeder from "../../../../helpers/product-seeder"
|
||||
|
||||
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||
import { MedusaV2Flag } from "@medusajs/utils"
|
||||
@@ -68,7 +67,6 @@ describe.skip("/admin/products", () => {
|
||||
|
||||
describe("POST /admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await createDefaultRuleTypes(medusaContainer)
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { workflowEngineTestSuite } from "./tests"
|
||||
|
||||
jest.setTimeout(5000000)
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: false,
|
||||
}
|
||||
|
||||
workflowEngineTestSuite(env, { force_modules_migration: true })
|
||||
@@ -1,13 +0,0 @@
|
||||
let ignore = [`**/dist`]
|
||||
|
||||
// Jest needs to compile this code, but generally we don't want this copied
|
||||
// to output folders
|
||||
if (process.env.NODE_ENV !== `test`) {
|
||||
ignore.push(`**/__tests__`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sourceMaps: true,
|
||||
presets: ["babel-preset-medusa-package"],
|
||||
ignore,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
dist/
|
||||
node_modules
|
||||
*yarn-error.log
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { Region } from "@medusajs/medusa"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../../environment-helpers/use-api"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
import {
|
||||
simpleProductFactory,
|
||||
simpleSalesChannelFactory,
|
||||
} from "../../../../factories"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
describe.skip("/store/carts", () => {
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
const doAfterEach = async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd, env } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
describe("POST /store/carts", () => {
|
||||
let prod1
|
||||
let prodSale
|
||||
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager
|
||||
await manager.insert(Region, {
|
||||
id: "region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
})
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country"
|
||||
SET region_id='region'
|
||||
WHERE iso_2 = 'us'`
|
||||
)
|
||||
|
||||
prod1 = await simpleProductFactory(dbConnection, {
|
||||
id: "test-product",
|
||||
variants: [{ id: "test-variant_1" }],
|
||||
})
|
||||
|
||||
prodSale = await simpleProductFactory(dbConnection, {
|
||||
id: "test-product-sale",
|
||||
variants: [
|
||||
{
|
||||
id: "test-variant-sale",
|
||||
prices: [{ amount: 1000, currency: "usd" }],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
id: "amazon-sc",
|
||||
name: "Amazon store",
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await doAfterEach()
|
||||
})
|
||||
|
||||
it("should create a cart in a sales channel", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.post("/store/carts", {
|
||||
sales_channel_id: "amazon-sc",
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
const getRes = await api.get(`/store/carts/${response.data.cart.id}`)
|
||||
expect(getRes.status).toEqual(200)
|
||||
expect(getRes.data.cart.sales_channel.id).toEqual("amazon-sc")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,212 +0,0 @@
|
||||
import {
|
||||
MoneyAmount,
|
||||
PriceList,
|
||||
ProductVariantMoneyAmount,
|
||||
Region,
|
||||
} from "@medusajs/medusa"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../../environment-helpers/use-api"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
import { simpleProductFactory } from "../../../../factories"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/carts", () => {
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
const doAfterEach = async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
describe("POST /store/carts", () => {
|
||||
let prod1
|
||||
let prodSale
|
||||
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager
|
||||
await manager.insert(Region, {
|
||||
id: "region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
})
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country"
|
||||
SET region_id='region'
|
||||
WHERE iso_2 = 'us'`
|
||||
)
|
||||
|
||||
prod1 = await simpleProductFactory(dbConnection, {
|
||||
id: "test-product",
|
||||
variants: [{ id: "test-variant_1" }],
|
||||
})
|
||||
|
||||
prodSale = await simpleProductFactory(dbConnection, {
|
||||
id: "test-product-sale",
|
||||
variants: [
|
||||
{
|
||||
id: "test-variant-sale",
|
||||
prices: [{ amount: 1000, currency: "usd" }],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await doAfterEach()
|
||||
})
|
||||
|
||||
it("should create a cart", async () => {
|
||||
const api = useApi()
|
||||
const response = await api.post("/store/carts")
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
const getRes = await api.post(`/store/carts/${response.data.cart.id}`)
|
||||
expect(getRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("should fail to create a cart when no region exist", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`UPDATE "country"
|
||||
SET region_id=null
|
||||
WHERE iso_2 = 'us'`
|
||||
)
|
||||
|
||||
await dbConnection.manager.query(`DELETE from region`)
|
||||
|
||||
try {
|
||||
await api.post("/store/carts")
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"A region is required to create a cart"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should create a cart with items", async () => {
|
||||
const yesterday = ((today) =>
|
||||
new Date(today.setDate(today.getDate() - 1)))(new Date())
|
||||
const tomorrow = ((today) =>
|
||||
new Date(today.setDate(today.getDate() + 1)))(new Date())
|
||||
|
||||
const priceList1 = await dbConnection.manager.create(PriceList, {
|
||||
id: "pl_current",
|
||||
name: "Past winter sale",
|
||||
description: "Winter sale for key accounts.",
|
||||
type: "sale",
|
||||
status: "active",
|
||||
starts_at: yesterday,
|
||||
ends_at: tomorrow,
|
||||
})
|
||||
|
||||
await dbConnection.manager.save(priceList1)
|
||||
|
||||
const ma_sale_1 = dbConnection.manager.create(MoneyAmount, {
|
||||
currency_code: "usd",
|
||||
amount: 800,
|
||||
price_list_id: "pl_current",
|
||||
})
|
||||
|
||||
await dbConnection.manager.save(ma_sale_1)
|
||||
|
||||
await dbConnection.manager.insert(ProductVariantMoneyAmount, {
|
||||
id: "pvma-test",
|
||||
variant_id: prodSale.variants[0].id,
|
||||
money_amount_id: ma_sale_1.id,
|
||||
})
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/carts", {
|
||||
items: [
|
||||
{
|
||||
variant_id: prod1.variants[0].id,
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
variant_id: prodSale.variants[0].id,
|
||||
quantity: 2,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
|
||||
response.data.cart.items.sort((a, b) => a.quantity - b.quantity)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.cart.items).toHaveLength(2)
|
||||
expect(response.data.cart.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
variant_id: prod1.variants[0].id,
|
||||
quantity: 1,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: prodSale.variants[0].id,
|
||||
quantity: 2,
|
||||
unit_price: 800,
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const getRes = await api.post(`/store/carts/${response.data.cart.id}`)
|
||||
expect(getRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("should create a cart with country", async () => {
|
||||
const api = useApi()
|
||||
const response = await api.post("/store/carts", {
|
||||
country_code: "us",
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.cart.shipping_address.country_code).toEqual("us")
|
||||
|
||||
const getRes = await api.post(`/store/carts/${response.data.cart.id}`)
|
||||
expect(getRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("should create a cart with context", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.post("/store/carts", {
|
||||
context: {
|
||||
test_id: "test",
|
||||
},
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
const getRes = await api.post(`/store/carts/${response.data.cart.id}`)
|
||||
expect(getRes.status).toEqual(200)
|
||||
|
||||
const cart = getRes.data.cart
|
||||
expect(cart.context).toEqual({
|
||||
ip: expect.any(String),
|
||||
user_agent: expect.stringContaining("axios/0.21."),
|
||||
test_id: "test",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,362 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
const cartSeeder = require("../../../../helpers/cart-seeder")
|
||||
const { simpleProductFactory } = require("../../../../factories")
|
||||
const { simpleSalesChannelFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
jest.setTimeout(60000)
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("/store/carts", () => {
|
||||
let shutdownServer
|
||||
let appContainer
|
||||
let dbConnection
|
||||
|
||||
let variantId
|
||||
let inventoryItemId
|
||||
let locationId
|
||||
|
||||
const doAfterEach = async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("POST /store/carts/:id", () => {
|
||||
beforeEach(async () => {
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
id: "test-channel",
|
||||
is_default: true,
|
||||
})
|
||||
|
||||
await adminSeeder(dbConnection)
|
||||
await cartSeeder(dbConnection, { sales_channel_id: "test-channel" })
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: "product1",
|
||||
sales_channels: [{ id: "test-channel" }],
|
||||
variants: [],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
// Add payment provider
|
||||
await api.post(
|
||||
`/admin/regions/test-region/payment-providers`,
|
||||
{
|
||||
provider_id: "test-pay",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const prodVarInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/products/product1/variants`,
|
||||
{
|
||||
title: "Test Variant w. inventory",
|
||||
sku: "MY_SKU",
|
||||
material: "material",
|
||||
origin_country: "UK",
|
||||
hs_code: "hs001",
|
||||
mid_code: "mids",
|
||||
weight: 300,
|
||||
length: 100,
|
||||
height: 200,
|
||||
width: 150,
|
||||
options: [
|
||||
{
|
||||
option_id: "product1-option",
|
||||
value: "SS",
|
||||
},
|
||||
],
|
||||
manage_inventory: true,
|
||||
prices: [{ currency_code: "usd", amount: 2300 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const variant = response.data.product.variants[0]
|
||||
|
||||
variantId = variant.id
|
||||
|
||||
const inventoryItems =
|
||||
await prodVarInventoryService.listInventoryItemsByVariant(variantId)
|
||||
|
||||
inventoryItemId = inventoryItems[0].id
|
||||
|
||||
// Add Stock location
|
||||
const stockRes = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
name: "Fake Warehouse",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
locationId = stockRes.data.stock_location.id
|
||||
|
||||
// Add stock level
|
||||
await api.post(
|
||||
`/admin/inventory-items/${inventoryItemId}/location-levels`,
|
||||
{
|
||||
location_id: locationId,
|
||||
stocked_quantity: 5,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
// Associate Stock Location with sales channel
|
||||
await api.post(
|
||||
`/admin/sales-channels/test-channel/stock-locations`,
|
||||
{
|
||||
location_id: locationId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await doAfterEach()
|
||||
})
|
||||
|
||||
it("reserve quantity when completing the cart", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartId = "test-cart"
|
||||
|
||||
// Add standard line item to cart
|
||||
await api.post(
|
||||
`/store/carts/${cartId}/line-items`,
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 3,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
|
||||
await api.post(`/store/carts/${cartId}/payment-sessions`)
|
||||
await api.post(`/store/carts/${cartId}/payment-session`, {
|
||||
provider_id: "test-pay",
|
||||
})
|
||||
|
||||
const getRes = await api.post(`/store/carts/${cartId}/complete`)
|
||||
|
||||
expect(getRes.status).toEqual(200)
|
||||
expect(getRes.data.type).toEqual("order")
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const stockLevel = await inventoryService.retrieveInventoryLevel(
|
||||
inventoryItemId,
|
||||
locationId
|
||||
)
|
||||
|
||||
expect(stockLevel.location_id).toEqual(locationId)
|
||||
expect(stockLevel.inventory_item_id).toEqual(inventoryItemId)
|
||||
expect(stockLevel.reserved_quantity).toEqual(3)
|
||||
expect(stockLevel.stocked_quantity).toEqual(5)
|
||||
})
|
||||
|
||||
it("removes reserved quantity when failing to complete the cart", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartRes = await api.post(
|
||||
`/store/carts`,
|
||||
{
|
||||
region_id: "test-region",
|
||||
items: [
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
|
||||
const cartId = cartRes.data.cart.id
|
||||
|
||||
await api.post(`/store/carts/${cartId}/payment-sessions`)
|
||||
await api.post(`/store/carts/${cartId}/payment-session`, {
|
||||
provider_id: "test-pay",
|
||||
})
|
||||
|
||||
const getRes = await api
|
||||
.post(`/store/carts/${cartId}/complete`)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(getRes.response.status).toEqual(400)
|
||||
expect(getRes.response.data).toEqual({
|
||||
type: "invalid_data",
|
||||
message: "Cannot create an order from the cart without a customer",
|
||||
})
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const [, count] = await inventoryService.listReservationItems({
|
||||
line_item_id: cartRes.data.cart.items.map((i) => i.id),
|
||||
})
|
||||
expect(count).toEqual(0)
|
||||
})
|
||||
|
||||
it("should decorate line item variant inventory_quantity when creating a line-item", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartId = "test-cart"
|
||||
|
||||
// Add standard line item to cart
|
||||
const addCart = await api
|
||||
.post(
|
||||
`/store/carts/${cartId}/line-items`,
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 3,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(addCart.status).toEqual(200)
|
||||
expect(addCart.data.cart.items[0].variant.inventory_quantity).toEqual(5)
|
||||
})
|
||||
|
||||
it("should decorate line item variant inventory_quantity when getting cart", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartId = "test-cart"
|
||||
|
||||
// Add standard line item to cart
|
||||
await api
|
||||
.post(
|
||||
`/store/carts/${cartId}/line-items`,
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 3,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
const cartResponse = await api
|
||||
.get(`/store/carts/${cartId}`, { withCredentials: true })
|
||||
.catch((e) => e)
|
||||
|
||||
expect(cartResponse.status).toEqual(200)
|
||||
expect(
|
||||
cartResponse.data.cart.items[0].variant.inventory_quantity
|
||||
).toEqual(5)
|
||||
})
|
||||
|
||||
it("fails to add a item on the cart if the inventory isn't enough", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartId = "test-cart"
|
||||
|
||||
// Add standard line item to cart
|
||||
const addCart = await api
|
||||
.post(
|
||||
`/store/carts/${cartId}/line-items`,
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 6,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(addCart.response.status).toEqual(400)
|
||||
expect(addCart.response.data.code).toEqual("insufficient_inventory")
|
||||
expect(addCart.response.data.message).toEqual(
|
||||
`Variant with id: ${variantId} does not have the required inventory`
|
||||
)
|
||||
})
|
||||
|
||||
it("fails to complete cart with items inventory not covered", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartId = "test-cart"
|
||||
|
||||
// Add standard line item to cart
|
||||
await api.post(
|
||||
`/store/carts/${cartId}/line-items`,
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 5,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
|
||||
await api.post(`/store/carts/${cartId}/payment-sessions`)
|
||||
await api.post(`/store/carts/${cartId}/payment-session`, {
|
||||
provider_id: "test-pay",
|
||||
})
|
||||
|
||||
// Another proccess reserves items before the cart is completed
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
inventoryService.createReservationItem({
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItemId,
|
||||
location_id: locationId,
|
||||
quantity: 2,
|
||||
})
|
||||
|
||||
const completeCartRes = await api
|
||||
.post(`/store/carts/${cartId}/complete`)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(completeCartRes.response.status).toEqual(409)
|
||||
expect(completeCartRes.response.data.errors[0].code).toEqual(
|
||||
"insufficient_inventory"
|
||||
)
|
||||
expect(completeCartRes.response.data.errors[0].message).toEqual(
|
||||
`Variant with id: ${variantId} does not have the required inventory`
|
||||
)
|
||||
|
||||
const stockLevel = await inventoryService.retrieveInventoryLevel(
|
||||
inventoryItemId,
|
||||
locationId
|
||||
)
|
||||
|
||||
expect(stockLevel.location_id).toEqual(locationId)
|
||||
expect(stockLevel.inventory_item_id).toEqual(inventoryItemId)
|
||||
expect(stockLevel.reserved_quantity).toEqual(2)
|
||||
expect(stockLevel.stocked_quantity).toEqual(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,86 +0,0 @@
|
||||
const path = require("path")
|
||||
const { ProductVariantInventoryService } = require("@medusajs/medusa")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Inventory Items endpoints", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
|
||||
// Set feature flag
|
||||
const flagRouter = appContainer.resolve("featureFlagRouter")
|
||||
flagRouter.setFlag("many_to_many_inventory", true)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const flagRouter = appContainer.resolve("featureFlagRouter")
|
||||
flagRouter.setFlag("many_to_many_inventory", false)
|
||||
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
it("should create inventory item without variant id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await api.post(
|
||||
`/admin/inventory-items`,
|
||||
{
|
||||
sku: "TABLE_LEG",
|
||||
description: "Table Leg",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
/** @type {ProductVariantInventoryService} */
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const inventoryItems = await inventoryService.list()
|
||||
|
||||
expect(inventoryItems.length).toEqual(1)
|
||||
|
||||
const variants = await productVariantInventoryService.listByItem([
|
||||
inventoryItems[0].id,
|
||||
])
|
||||
expect(variants.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,180 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleCustomerFactory,
|
||||
} = require("../../../../factories")
|
||||
const {
|
||||
simpleRegionFactory,
|
||||
simpleShippingOptionFactory,
|
||||
} = require("../../../../factories")
|
||||
const {
|
||||
simpleAddressFactory,
|
||||
} = require("../../../../factories/simple-address-factory")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("/store/carts", () => {
|
||||
let shutdownServer
|
||||
let appContainer
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
beforeEach(async () => {})
|
||||
|
||||
describe("POST /store/carts", () => {
|
||||
const variantId = "test-variant"
|
||||
|
||||
let region
|
||||
let invItemId
|
||||
let prodVarInventoryService
|
||||
let inventoryService
|
||||
let lineItemService
|
||||
let stockLocationService
|
||||
let salesChannelLocationService
|
||||
|
||||
let address
|
||||
let shippingOption
|
||||
let customer
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
const api = useApi()
|
||||
|
||||
prodVarInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
lineItemService = appContainer.resolve("lineItemService")
|
||||
inventoryService = appContainer.resolve("inventoryService")
|
||||
stockLocationService = appContainer.resolve("stockLocationService")
|
||||
salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
// create region
|
||||
region = await simpleRegionFactory(dbConnection, {})
|
||||
|
||||
// create product
|
||||
const product = await simpleProductFactory(dbConnection, {
|
||||
variants: [{ id: variantId }],
|
||||
})
|
||||
|
||||
const location = await stockLocationService.create({
|
||||
name: "test-location",
|
||||
})
|
||||
|
||||
const invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
invItemId = invItem.id
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
await prodVarInventoryService.attachInventoryItem(variantId, invItem.id)
|
||||
|
||||
// create customer
|
||||
customer = await simpleCustomerFactory(dbConnection, {})
|
||||
|
||||
address = await simpleAddressFactory(dbConnection, {})
|
||||
|
||||
// create shipping option
|
||||
shippingOption = await simpleShippingOptionFactory(dbConnection, {
|
||||
region_id: region.id,
|
||||
})
|
||||
})
|
||||
|
||||
it("should create the order from a draft order and shouldn't adjust reservations", async () => {
|
||||
const api = useApi()
|
||||
let inventoryItem = await api.get(
|
||||
`/admin/inventory-items/${invItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(inventoryItem.data.inventory_item.location_levels.length).toEqual(
|
||||
1
|
||||
)
|
||||
let locationLevel = inventoryItem.data.inventory_item.location_levels[0]
|
||||
|
||||
expect(locationLevel.stocked_quantity).toEqual(10)
|
||||
expect(locationLevel.reserved_quantity).toEqual(0)
|
||||
|
||||
const payload = {
|
||||
email: "test@test.dk",
|
||||
shipping_address: address.id,
|
||||
discounts: [],
|
||||
items: [
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 2,
|
||||
metadata: {},
|
||||
},
|
||||
],
|
||||
region_id: region.id,
|
||||
customer_id: customer.id,
|
||||
shipping_methods: [
|
||||
{
|
||||
option_id: shippingOption.id,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const createResponse = await api.post(
|
||||
"/admin/draft-orders",
|
||||
payload,
|
||||
adminHeaders
|
||||
)
|
||||
expect(createResponse.status).toEqual(200)
|
||||
|
||||
const registerPaymentResponse = await api.post(
|
||||
`/admin/draft-orders/${createResponse.data.draft_order.id}/pay`,
|
||||
payload,
|
||||
adminHeaders
|
||||
)
|
||||
expect(registerPaymentResponse.status).toEqual(200)
|
||||
|
||||
inventoryItem = await api.get(
|
||||
`/admin/inventory-items/${invItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(inventoryItem.data.inventory_item.location_levels.length).toEqual(
|
||||
1
|
||||
)
|
||||
locationLevel = inventoryItem.data.inventory_item.location_levels[0]
|
||||
|
||||
expect(locationLevel.stocked_quantity).toEqual(10)
|
||||
expect(locationLevel.reserved_quantity).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,176 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const {
|
||||
ProductVariantInventoryService,
|
||||
ProductVariantService,
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { simpleProductFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
describe("Create Variant", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
it("When creating a new product variant it should create an Inventory Item", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: "test-product",
|
||||
variants: [{ id: "test-variant" }],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/products/test-product/variants`,
|
||||
{
|
||||
title: "Test Variant w. inventory",
|
||||
sku: "MY_SKU",
|
||||
material: "material",
|
||||
origin_country: "UK",
|
||||
hs_code: "hs001",
|
||||
mid_code: "mids",
|
||||
weight: 300,
|
||||
length: 100,
|
||||
height: 200,
|
||||
width: 150,
|
||||
manage_inventory: true,
|
||||
options: [
|
||||
{
|
||||
option_id: "test-product-option",
|
||||
value: "SS",
|
||||
},
|
||||
],
|
||||
prices: [{ currency_code: "usd", amount: 2300 }],
|
||||
},
|
||||
{ headers: { "x-medusa-access-token": "test_token" } }
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
const variantId = response.data.product.variants.find(
|
||||
(v) => v.id !== "test-variant"
|
||||
).id
|
||||
|
||||
const variantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
const inventory =
|
||||
await variantInventoryService.listInventoryItemsByVariant(variantId)
|
||||
|
||||
expect(inventory).toHaveLength(1)
|
||||
expect(inventory).toEqual([
|
||||
expect.objectContaining({
|
||||
origin_country: "UK",
|
||||
hs_code: "hs001",
|
||||
mid_code: "mids",
|
||||
weight: 300,
|
||||
length: 100,
|
||||
height: 200,
|
||||
width: 150,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("When creating a new variant fails, it should revert all the transaction", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: "test-product",
|
||||
variants: [{ id: "test-variant" }],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
jest
|
||||
.spyOn(ProductVariantInventoryService.prototype, "attachInventoryItem")
|
||||
.mockImplementation(() => {
|
||||
throw new Error("Failure while attaching inventory item")
|
||||
})
|
||||
|
||||
const prodVariantDeleteMock = jest.spyOn(
|
||||
ProductVariantService.prototype,
|
||||
"delete"
|
||||
)
|
||||
|
||||
const error = await api
|
||||
.post(
|
||||
`/admin/products/test-product/variants`,
|
||||
{
|
||||
title: "Test Variant w. inventory",
|
||||
sku: "MY_SKU",
|
||||
material: "material",
|
||||
origin_country: "UK",
|
||||
hs_code: "hs001",
|
||||
mid_code: "mids",
|
||||
weight: 300,
|
||||
length: 100,
|
||||
height: 200,
|
||||
width: 150,
|
||||
manage_inventory: true,
|
||||
options: [
|
||||
{
|
||||
option_id: "test-product-option",
|
||||
value: "SS",
|
||||
},
|
||||
],
|
||||
prices: [{ currency_code: "usd", amount: 2300 }],
|
||||
},
|
||||
{ headers: { "x-medusa-access-token": "test_token" } }
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Failure while attaching inventory item"
|
||||
)
|
||||
|
||||
expect(prodVariantDeleteMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,114 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { simpleProductFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
describe("Delete Variant", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
it("When deleting a product variant it removes the inventory items associated to it", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: "test-product",
|
||||
variants: [{ id: "test-variant" }],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/products/test-product/variants`,
|
||||
{
|
||||
title: "Test Variant w. inventory",
|
||||
sku: "MY_SKU",
|
||||
manage_inventory: true,
|
||||
options: [
|
||||
{
|
||||
option_id: "test-product-option",
|
||||
value: "SS",
|
||||
},
|
||||
],
|
||||
prices: [{ currency_code: "usd", amount: 2300 }],
|
||||
},
|
||||
{ headers: { "x-medusa-access-token": "test_token" } }
|
||||
)
|
||||
|
||||
const variantId = response.data.product.variants.find(
|
||||
(v) => v.sku === "MY_SKU"
|
||||
).id
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const variantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
const variantService = appContainer.resolve("productVariantService")
|
||||
|
||||
const invItem2 = await inventoryService.createInventoryItem({
|
||||
sku: "123456",
|
||||
})
|
||||
|
||||
await variantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem2.id,
|
||||
2
|
||||
)
|
||||
|
||||
expect(
|
||||
await variantInventoryService.listInventoryItemsByVariant(variantId)
|
||||
).toHaveLength(2)
|
||||
|
||||
await api.delete(`/admin/products/test-product/variants/${variantId}`, {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
})
|
||||
|
||||
await expect(variantService.retrieve(variantId)).rejects.toThrow(
|
||||
`Variant with id: ${variantId} was not found`
|
||||
)
|
||||
|
||||
expect(
|
||||
await variantInventoryService.listInventoryItemsByVariant(variantId)
|
||||
).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,151 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleSalesChannelFactory,
|
||||
} = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Get products", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
const productId = "test-product"
|
||||
const variantId = "test-variant"
|
||||
let invItem
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const locationService = appContainer.resolve("stockLocationService")
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||
is_default: true,
|
||||
})
|
||||
|
||||
const location = await locationService.create({ name: "test-location" })
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: productId,
|
||||
status: "published",
|
||||
variants: [{ id: variantId }],
|
||||
},
|
||||
100
|
||||
)
|
||||
await salesChannelService.addProducts(salesChannel.id, [productId])
|
||||
await salesChannelLocationService.associateLocation(
|
||||
salesChannel.id,
|
||||
location.id
|
||||
)
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem.id
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 100,
|
||||
})
|
||||
})
|
||||
|
||||
describe("/store/products/:id", () => {
|
||||
it("Expands inventory items when getting product with expand parameters", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(
|
||||
`/store/products/${productId}?expand=variants,variants.inventory_items`
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
id: variantId,
|
||||
inventory_items: [
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem.id,
|
||||
variant_id: variantId,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("/admin/products/:id", () => {
|
||||
it("should get inventory quantity for products fetched through the admin api", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(`/admin/products/${productId}`, adminHeaders)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
id: variantId,
|
||||
inventory_quantity: 100,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,144 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleSalesChannelFactory,
|
||||
} = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Get variant", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
const productId = "test-product"
|
||||
const variantId = "test-variant"
|
||||
let invItem
|
||||
let salesChannelService
|
||||
let salesChannelLocationService
|
||||
let location
|
||||
let inventoryService
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
inventoryService = appContainer.resolve("inventoryService")
|
||||
salesChannelService = appContainer.resolve("salesChannelService")
|
||||
salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
|
||||
location = await stockLocationService.create({
|
||||
name: "test-location",
|
||||
})
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: productId,
|
||||
status: "published",
|
||||
variants: [{ id: variantId }],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem.id
|
||||
)
|
||||
})
|
||||
|
||||
it("Expands inventory items when getting variant with expand parameters", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(
|
||||
`/store/variants/${variantId}?expand=inventory_items`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.variant).toEqual(
|
||||
expect.objectContaining({
|
||||
id: variantId,
|
||||
inventory_items: [
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem.id,
|
||||
variant_id: variantId,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("sets availability correctly", async () => {
|
||||
const salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||
is_default: true,
|
||||
})
|
||||
|
||||
await salesChannelService.addProducts(salesChannel.id, [productId])
|
||||
|
||||
await salesChannelLocationService.associateLocation(
|
||||
salesChannel.id,
|
||||
location.id
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.get(`/store/variants/${variantId}`)
|
||||
|
||||
expect(response.data).toEqual({
|
||||
variant: expect.objectContaining({
|
||||
purchasable: true,
|
||||
inventory_quantity: 10,
|
||||
}),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,348 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { simpleProductFactory } = require("../../../../factories")
|
||||
const { simpleSalesChannelFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Create Variant", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("list-products", () => {
|
||||
const productId = "test-product"
|
||||
const variantId = "test-variant"
|
||||
let sc2
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: productId,
|
||||
status: "published",
|
||||
sales_channels: [],
|
||||
variants: [{ id: variantId }],
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
const sc1 = await simpleSalesChannelFactory(dbConnection, {})
|
||||
sc2 = await simpleSalesChannelFactory(dbConnection, {})
|
||||
const sc3 = await simpleSalesChannelFactory(dbConnection, {})
|
||||
|
||||
const sl1 = await stockLocationService.create({ name: "sl1" })
|
||||
const sl2 = await stockLocationService.create({ name: "sl2" })
|
||||
|
||||
await salesChannelLocationService.associateLocation(sc1.id, sl1.id)
|
||||
await salesChannelLocationService.associateLocation(sc2.id, sl1.id)
|
||||
await salesChannelLocationService.associateLocation(sc2.id, sl2.id)
|
||||
|
||||
const invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem.id
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: sl1.id,
|
||||
stocked_quantity: 3,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: sl2.id,
|
||||
stocked_quantity: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it("lists location availability correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(`/admin/products`, adminHeaders)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
inventory_quantity: 4,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
describe("/store/products", () => {
|
||||
beforeEach(async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: `${productId}-1`,
|
||||
status: "published",
|
||||
sales_channels: [],
|
||||
variants: [
|
||||
{
|
||||
id: `${variantId}-1`,
|
||||
manage_inventory: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
101
|
||||
)
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: `${productId}-2`,
|
||||
status: "published",
|
||||
variants: [{ id: `${variantId}-2`, manage_inventory: true }],
|
||||
sales_channels: [],
|
||||
},
|
||||
102
|
||||
)
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: `${productId}-3`,
|
||||
status: "published",
|
||||
sales_channels: [],
|
||||
variants: [
|
||||
{
|
||||
id: `${variantId}-3`,
|
||||
manage_inventory: true,
|
||||
allow_backorder: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
103
|
||||
)
|
||||
const invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-1",
|
||||
})
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
`${variantId}-3`,
|
||||
invItem.id
|
||||
)
|
||||
await simpleProductFactory(
|
||||
dbConnection,
|
||||
{
|
||||
id: `${productId}-4`,
|
||||
status: "published",
|
||||
sales_channels: [],
|
||||
variants: [
|
||||
{
|
||||
id: `${variantId}-4`,
|
||||
manage_inventory: true,
|
||||
allow_backorder: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
104
|
||||
)
|
||||
const invItem1 = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-2",
|
||||
})
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
`${variantId}-4`,
|
||||
invItem1.id
|
||||
)
|
||||
})
|
||||
|
||||
it("includes inventory items when property is expanded", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const result = await api.get(
|
||||
`/store/products?expand=variants,variants.inventory_items`
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.products).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_items: [expect.any(Object)],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("lists location availability correctly for store", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(`/store/products`)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: `${productId}-4`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-3`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-2`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-1`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
inventory_quantity: 10,
|
||||
purchasable: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("lists location availability correctly for store with sales channel id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const productService = appContainer.resolve("productService")
|
||||
|
||||
const ids = [
|
||||
`${productId}`,
|
||||
`${productId}-1`,
|
||||
`${productId}-2`,
|
||||
`${productId}-3`,
|
||||
`${productId}-4`,
|
||||
]
|
||||
|
||||
for (const id of ids) {
|
||||
await productService.update(id, {
|
||||
sales_channels: [{ id: sc2.id }],
|
||||
})
|
||||
}
|
||||
|
||||
const res = await api.get(
|
||||
`/store/products?sales_channel_id[]=${sc2.id}`
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: `${productId}-4`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-3`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-2`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: `${productId}-1`,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
inventory_quantity: 10,
|
||||
purchasable: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: true,
|
||||
inventory_quantity: 4,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,147 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { simpleProductFactory } = require("../../../../factories")
|
||||
const { simpleSalesChannelFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("List Variants", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
const variantId = "test-variant"
|
||||
let invItem
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
const prodVarInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
const location = await stockLocationService.create({
|
||||
name: "test-location",
|
||||
})
|
||||
|
||||
const salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||
is_default: true,
|
||||
})
|
||||
|
||||
const product = await simpleProductFactory(dbConnection, {
|
||||
variants: [{ id: variantId }],
|
||||
})
|
||||
|
||||
await salesChannelService.addProducts(salesChannel.id, [product.id])
|
||||
await salesChannelLocationService.associateLocation(
|
||||
salesChannel.id,
|
||||
location.id
|
||||
)
|
||||
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
const invItemId = invItem.id
|
||||
|
||||
await prodVarInventoryService.attachInventoryItem(variantId, invItem.id)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it("Decorates inventory quantities when listing variants", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const listVariantsRes = await api.get(`/admin/variants`, adminHeaders)
|
||||
|
||||
expect(listVariantsRes.status).toEqual(200)
|
||||
expect(listVariantsRes.data.variants.length).toEqual(1)
|
||||
expect(listVariantsRes.data.variants[0]).toEqual(
|
||||
expect.objectContaining({ id: variantId, inventory_quantity: 10 })
|
||||
)
|
||||
})
|
||||
|
||||
it("expands inventory_items when querying with expand parameter", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const listVariantsRes = await api.get(
|
||||
`/admin/variants?expand=inventory_items`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(listVariantsRes.status).toEqual(200)
|
||||
expect(listVariantsRes.data.variants.length).toEqual(1)
|
||||
expect(listVariantsRes.data.variants[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: variantId,
|
||||
inventory_items: [
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem.id,
|
||||
variant_id: variantId,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("sets availability correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.get(`/store/variants?ids=${variantId}`)
|
||||
|
||||
expect(response.data).toEqual({
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
purchasable: true,
|
||||
inventory_quantity: 10,
|
||||
}),
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,459 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleOrderFactory,
|
||||
simpleRegionFactory,
|
||||
} = require("../../../../factories")
|
||||
const { simpleSalesChannelFactory } = require("../../../../factories")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Inventory Items endpoints", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
let inventoryItem
|
||||
let locationId
|
||||
|
||||
let prodVarInventoryService
|
||||
let inventoryService
|
||||
let stockLocationService
|
||||
let salesChannelLocationService
|
||||
|
||||
let reg
|
||||
let regionId
|
||||
let order
|
||||
let variantId
|
||||
let reservationItem
|
||||
let lineItemId
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
const api = useApi()
|
||||
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
prodVarInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
inventoryService = appContainer.resolve("inventoryService")
|
||||
stockLocationService = appContainer.resolve("stockLocationService")
|
||||
salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const r = await simpleRegionFactory(dbConnection, {})
|
||||
regionId = r.id
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
id: "test-channel",
|
||||
is_default: true,
|
||||
})
|
||||
|
||||
await simpleProductFactory(dbConnection, {
|
||||
id: "product1",
|
||||
sales_channels: [{ id: "test-channel" }],
|
||||
})
|
||||
|
||||
const productRes = await api.get(`/admin/products/product1`, adminHeaders)
|
||||
|
||||
variantId = productRes.data.product.variants[0].id
|
||||
|
||||
const stockRes = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
name: "Fake Warehouse",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
locationId = stockRes.data.stock_location.id
|
||||
|
||||
await salesChannelLocationService.associateLocation(
|
||||
"test-channel",
|
||||
locationId
|
||||
)
|
||||
|
||||
inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "1234",
|
||||
})
|
||||
|
||||
await prodVarInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
inventoryItem.id
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 100,
|
||||
})
|
||||
|
||||
order = await simpleOrderFactory(dbConnection, {
|
||||
sales_channel: "test-channel",
|
||||
line_items: [
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 2,
|
||||
id: "line-item-id",
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
shipping_option: {
|
||||
region_id: r.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const orderRes = await api.get(`/admin/orders/${order.id}`, adminHeaders)
|
||||
|
||||
lineItemId = orderRes.data.order.items[0].id
|
||||
|
||||
reservationItem = await inventoryService.createReservationItem({
|
||||
line_item_id: lineItemId,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 2,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Reservation items", () => {
|
||||
it("Create reservation item throws if available item quantity is less than reservation quantity", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const orderRes = await api.get(`/admin/orders/${order.id}`, adminHeaders)
|
||||
|
||||
expect(orderRes.data.order.items[0].quantity).toBe(2)
|
||||
expect(orderRes.data.order.items[0].fulfilled_quantity).toBeFalsy()
|
||||
|
||||
const payload = {
|
||||
quantity: 1,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
line_item_id: lineItemId,
|
||||
location_id: locationId,
|
||||
}
|
||||
|
||||
const res = await api
|
||||
.post(`/admin/reservations`, payload, adminHeaders)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(res.response.status).toBe(400)
|
||||
expect(res.response.data).toEqual({
|
||||
type: "invalid_data",
|
||||
message:
|
||||
"The reservation quantity cannot be greater than the unfulfilled line item quantity",
|
||||
})
|
||||
})
|
||||
|
||||
it("Update reservation item throws if available item quantity is less than reservation quantity", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const orderRes = await api.get(`/admin/orders/${order.id}`, adminHeaders)
|
||||
|
||||
expect(orderRes.data.order.items[0].quantity).toBe(2)
|
||||
expect(orderRes.data.order.items[0].fulfilled_quantity).toBeFalsy()
|
||||
|
||||
const payload = {
|
||||
quantity: 3,
|
||||
}
|
||||
|
||||
const res = await api
|
||||
.post(
|
||||
`/admin/reservations/${reservationItem.id}`,
|
||||
payload,
|
||||
adminHeaders
|
||||
)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(res.response.status).toBe(400)
|
||||
expect(res.response.data).toEqual({
|
||||
type: "invalid_data",
|
||||
message:
|
||||
"The reservation quantity cannot be greater than the unfulfilled line item quantity",
|
||||
})
|
||||
})
|
||||
|
||||
describe("List reservation items", () => {
|
||||
let item2
|
||||
let location2
|
||||
let reservation2
|
||||
|
||||
beforeEach(async () => {
|
||||
const api = useApi()
|
||||
const stockRes = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
name: "Fake Warehouse 1",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
location2 = stockRes.data.stock_location.id
|
||||
|
||||
await salesChannelLocationService.associateLocation(
|
||||
"test-channel",
|
||||
location2
|
||||
)
|
||||
|
||||
const inventoryItem1 = await inventoryService.createInventoryItem({
|
||||
sku: "12345",
|
||||
})
|
||||
item2 = inventoryItem1.id
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: item2,
|
||||
location_id: location2,
|
||||
stocked_quantity: 100,
|
||||
})
|
||||
|
||||
order = await simpleOrderFactory(dbConnection, {
|
||||
sales_channel: "test-channel",
|
||||
line_items: [
|
||||
{
|
||||
variant_id: variantId,
|
||||
quantity: 2,
|
||||
id: "line-item-id-2",
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
shipping_option: {
|
||||
region_id: regionId,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const orderRes = await api.get(
|
||||
`/admin/orders/${order.id}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
reservation2 = await inventoryService.createReservationItem({
|
||||
line_item_id: "line-item-id-2",
|
||||
inventory_item_id: item2,
|
||||
location_id: location2,
|
||||
description: "test description",
|
||||
quantity: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it("lists reservation items", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations`,
|
||||
adminHeaders
|
||||
)
|
||||
expect(reservationsRes.data.reservations.length).toBe(2)
|
||||
expect(reservationsRes.data.reservations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: reservationItem.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: reservation2.id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
describe("Filters reservation items", () => {
|
||||
it("filters by location", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?location_id[]=${locationId}`,
|
||||
adminHeaders
|
||||
)
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].location_id).toBe(
|
||||
locationId
|
||||
)
|
||||
})
|
||||
|
||||
it("filters by itemID", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?inventory_item_id[]=${item2}`,
|
||||
adminHeaders
|
||||
)
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].inventory_item_id).toBe(
|
||||
item2
|
||||
)
|
||||
})
|
||||
|
||||
it("filters by quantity", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?quantity[gt]=1`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(
|
||||
reservationItem.id
|
||||
)
|
||||
})
|
||||
|
||||
it("filters by date", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?created_at[gte]=${new Date(
|
||||
reservation2.created_at
|
||||
).toISOString()}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(reservation2.id)
|
||||
})
|
||||
|
||||
it("filters by description using equals", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api
|
||||
.get(
|
||||
`/admin/reservations?description=test%20description`,
|
||||
adminHeaders
|
||||
)
|
||||
.catch(console.log)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(reservation2.id)
|
||||
})
|
||||
|
||||
it("filters by description using equals removes results", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?description=description`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(0)
|
||||
})
|
||||
|
||||
it("filters by description using contains", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?description[contains]=descri`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(reservation2.id)
|
||||
})
|
||||
|
||||
it("filters by description using starts_with", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api
|
||||
.get(
|
||||
`/admin/reservations?description[starts_with]=test`,
|
||||
adminHeaders
|
||||
)
|
||||
.catch(console.log)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(reservation2.id)
|
||||
})
|
||||
|
||||
it("filters by description using starts_with removes results", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?description[starts_with]=description`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(0)
|
||||
})
|
||||
|
||||
it("filters by description using ends_with", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?description[ends_with]=test`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(0)
|
||||
})
|
||||
|
||||
it("filters by description using ends_with removes results", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const reservationsRes = await api.get(
|
||||
`/admin/reservations?description[ends_with]=description`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(reservationsRes.data.reservations.length).toBe(1)
|
||||
expect(reservationsRes.data.reservations[0].id).toBe(reservation2.id)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("lists reservations with inventory_items and line items", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api.get(
|
||||
`/admin/reservations?expand=line_item,inventory_item`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.reservations.length).toEqual(1)
|
||||
expect(res.data.reservations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item: expect.objectContaining({}),
|
||||
line_item: expect.objectContaining({
|
||||
order: expect.objectContaining({}),
|
||||
}),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,815 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../environment-helpers/use-db")
|
||||
const { getContainer } = require("../../../environment-helpers/use-container")
|
||||
const { useExpressServer } = require("../../../environment-helpers/use-api")
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
describe("Inventory Module", () => {
|
||||
let shutdownServer
|
||||
let appContainer
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Module Interface", () => {
|
||||
it("createInventoryItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
expect(inventoryItem).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
hs_code: "hs_code 123",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
requires_shipping: true,
|
||||
metadata: { abc: 123 },
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("updateInventoryItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const item = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
const updatedInventoryItem = await inventoryService.updateInventoryItem(
|
||||
item.id,
|
||||
{
|
||||
origin_country: "CZ",
|
||||
mid_code: "mid code 345",
|
||||
material: "lycra and polyester",
|
||||
weight: 500,
|
||||
metadata: {
|
||||
dce: 456,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(updatedInventoryItem).toEqual(
|
||||
expect.objectContaining({
|
||||
id: item.id,
|
||||
sku: item.sku,
|
||||
origin_country: "CZ",
|
||||
hs_code: item.hs_code,
|
||||
mid_code: "mid code 345",
|
||||
material: "lycra and polyester",
|
||||
weight: 500,
|
||||
length: item.length,
|
||||
height: item.height,
|
||||
width: item.width,
|
||||
requires_shipping: true,
|
||||
metadata: { dce: 456 },
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("deleteInventoryItem and retrieveInventoryItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const item = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.deleteInventoryItem(item.id)
|
||||
|
||||
const deletedItem = inventoryService.retrieveInventoryItem(item.id)
|
||||
|
||||
await expect(deletedItem).rejects.toThrow(
|
||||
`InventoryItem with id ${item.id} was not found`
|
||||
)
|
||||
})
|
||||
|
||||
it("createInventoryLevel", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
const inventoryLevel = await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 15,
|
||||
incoming_quantity: 4,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "second_location",
|
||||
stocked_quantity: 10,
|
||||
reserved_quantity: 1,
|
||||
})
|
||||
|
||||
expect(inventoryLevel).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
incoming_quantity: 4,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
metadata: null,
|
||||
reserved_quantity: 15,
|
||||
stocked_quantity: 50,
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveStockedQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
])
|
||||
).toEqual(50)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveAvailableQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
])
|
||||
).toEqual(35)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveReservedQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
])
|
||||
).toEqual(15)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveStockedQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
"second_location",
|
||||
])
|
||||
).toEqual(60)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveAvailableQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
"second_location",
|
||||
])
|
||||
).toEqual(44)
|
||||
|
||||
expect(
|
||||
await inventoryService.retrieveReservedQuantity(inventoryItem.id, [
|
||||
"location_123",
|
||||
"second_location",
|
||||
])
|
||||
).toEqual(16)
|
||||
})
|
||||
|
||||
it("updateInventoryLevel", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
const updatedLevel = await inventoryService.updateInventoryLevel(
|
||||
inventoryItem.id,
|
||||
"location_123",
|
||||
{
|
||||
stocked_quantity: 25,
|
||||
reserved_quantity: 4,
|
||||
incoming_quantity: 10,
|
||||
}
|
||||
)
|
||||
|
||||
expect(updatedLevel).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
incoming_quantity: 10,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
metadata: null,
|
||||
reserved_quantity: 4,
|
||||
stocked_quantity: 25,
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe("updateInventoryLevel", () => {
|
||||
it("should pass along the correct context when doing a bulk update", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
let error
|
||||
try {
|
||||
await inventoryService.updateInventoryLevels(
|
||||
[
|
||||
{
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 25,
|
||||
reserved_quantity: 4,
|
||||
incoming_quantity: 10,
|
||||
},
|
||||
],
|
||||
{
|
||||
transactionManager: {},
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toEqual("manager.getRepository is not a function")
|
||||
})
|
||||
|
||||
it("should pass along the correct context when doing a single update", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
let error
|
||||
try {
|
||||
await inventoryService.updateInventoryLevel(
|
||||
inventoryItem.id,
|
||||
"location_123",
|
||||
{
|
||||
stocked_quantity: 25,
|
||||
reserved_quantity: 4,
|
||||
incoming_quantity: 10,
|
||||
},
|
||||
{
|
||||
transactionManager: {},
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toEqual("manager.getRepository is not a function")
|
||||
})
|
||||
})
|
||||
|
||||
it("deleteInventoryLevel", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
const inventoryLevel = await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
await inventoryService.deleteInventoryLevel(
|
||||
inventoryItem.id,
|
||||
"location_123"
|
||||
)
|
||||
|
||||
const deletedLevel = inventoryService.retrieveInventoryLevel(
|
||||
inventoryItem.id,
|
||||
"location_123"
|
||||
)
|
||||
|
||||
await expect(deletedLevel).rejects.toThrow(
|
||||
`Inventory level for item ${inventoryItem.id} and location location_123 not found`
|
||||
)
|
||||
})
|
||||
|
||||
it("createReservationItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const locationId = "location_123"
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
})
|
||||
|
||||
const tryReserve = inventoryService.createReservationItem({
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 10,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(tryReserve).rejects.toThrow(
|
||||
`Item ${inventoryItem.id} is not stocked at location ${locationId}`
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
const inventoryReservation = await inventoryService.createReservationItem(
|
||||
{
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 10,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(inventoryReservation).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 10,
|
||||
metadata: { abc: 123 },
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
|
||||
const [available, reserved] = await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(inventoryItem.id, [
|
||||
locationId,
|
||||
]),
|
||||
inventoryService.retrieveReservedQuantity(inventoryItem.id, locationId),
|
||||
])
|
||||
|
||||
expect(available).toEqual(40)
|
||||
expect(reserved).toEqual(10)
|
||||
})
|
||||
|
||||
it("updateReservationItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const locationId = "location_123"
|
||||
const newLocationId = "location_new"
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: newLocationId,
|
||||
stocked_quantity: 20,
|
||||
reserved_quantity: 5,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
const inventoryReservation = await inventoryService.createReservationItem(
|
||||
{
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 15,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const [available, reserved] = await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(inventoryItem.id, locationId),
|
||||
])
|
||||
|
||||
expect(available).toEqual(35)
|
||||
expect(reserved).toEqual(15)
|
||||
|
||||
const updatedReservation = await inventoryService.updateReservationItem(
|
||||
inventoryReservation.id,
|
||||
{
|
||||
quantity: 5,
|
||||
}
|
||||
)
|
||||
|
||||
expect(updatedReservation).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 5,
|
||||
metadata: { abc: 123 },
|
||||
})
|
||||
)
|
||||
|
||||
const [newAvailable, newReserved] = await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(inventoryItem.id, locationId),
|
||||
])
|
||||
|
||||
expect(newAvailable).toEqual(45)
|
||||
expect(newReserved).toEqual(5)
|
||||
|
||||
const updatedReservationLocation =
|
||||
await inventoryService.updateReservationItem(inventoryReservation.id, {
|
||||
quantity: 12,
|
||||
location_id: newLocationId,
|
||||
})
|
||||
|
||||
expect(updatedReservationLocation).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: newLocationId,
|
||||
quantity: 12,
|
||||
metadata: { abc: 123 },
|
||||
})
|
||||
)
|
||||
|
||||
const [
|
||||
oldLocationAvailable,
|
||||
oldLocationReserved,
|
||||
newLocationAvailable,
|
||||
newLocationReserved,
|
||||
] = await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(inventoryItem.id, locationId),
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
newLocationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(
|
||||
inventoryItem.id,
|
||||
newLocationId
|
||||
),
|
||||
])
|
||||
|
||||
expect(oldLocationAvailable).toEqual(50)
|
||||
expect(oldLocationReserved).toEqual(0)
|
||||
expect(newLocationAvailable).toEqual(3)
|
||||
expect(newLocationReserved).toEqual(17)
|
||||
})
|
||||
|
||||
it("deleteReservationItem and deleteReservationItemsByLineItem", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const locationId = "location_123"
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
const inventoryReservation = await inventoryService.createReservationItem(
|
||||
{
|
||||
line_item_id: "line_item_123",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 1,
|
||||
}
|
||||
)
|
||||
|
||||
for (let quant = 1; quant <= 3; quant++) {
|
||||
await inventoryService.createReservationItem({
|
||||
line_item_id: "line_item_444",
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 1,
|
||||
})
|
||||
}
|
||||
|
||||
const [available, reserved] = await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(inventoryItem.id, locationId),
|
||||
])
|
||||
|
||||
expect(available).toEqual(6)
|
||||
expect(reserved).toEqual(4)
|
||||
|
||||
await inventoryService.deleteReservationItemsByLineItem("line_item_444")
|
||||
const [afterDeleteLineitemAvailable, afterDeleteLineitemReserved] =
|
||||
await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
])
|
||||
|
||||
expect(afterDeleteLineitemAvailable).toEqual(9)
|
||||
expect(afterDeleteLineitemReserved).toEqual(1)
|
||||
|
||||
await inventoryService.deleteReservationItem(inventoryReservation.id)
|
||||
const [afterDeleteReservationAvailable, afterDeleteReservationReserved] =
|
||||
await Promise.all([
|
||||
inventoryService.retrieveAvailableQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
inventoryService.retrieveReservedQuantity(
|
||||
inventoryItem.id,
|
||||
locationId
|
||||
),
|
||||
])
|
||||
|
||||
expect(afterDeleteReservationAvailable).toEqual(10)
|
||||
expect(afterDeleteReservationReserved).toEqual(0)
|
||||
})
|
||||
|
||||
it("confirmInventory", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const locationId = "location_123"
|
||||
const secondLocationId = "location_551"
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
reserved_quantity: 5,
|
||||
})
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: secondLocationId,
|
||||
stocked_quantity: 6,
|
||||
reserved_quantity: 1,
|
||||
})
|
||||
|
||||
expect(
|
||||
await inventoryService.confirmInventory(inventoryItem.id, locationId, 5)
|
||||
).toBeTruthy()
|
||||
|
||||
expect(
|
||||
await inventoryService.confirmInventory(
|
||||
inventoryItem.id,
|
||||
[locationId, secondLocationId],
|
||||
10
|
||||
)
|
||||
).toBeTruthy()
|
||||
|
||||
expect(
|
||||
await inventoryService.confirmInventory(inventoryItem.id, locationId, 6)
|
||||
).toBeFalsy()
|
||||
|
||||
expect(
|
||||
await inventoryService.confirmInventory(
|
||||
inventoryItem.id,
|
||||
[locationId, secondLocationId],
|
||||
11
|
||||
)
|
||||
).toBeFalsy()
|
||||
})
|
||||
|
||||
it("adjustInventory", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const locationId = "location_123"
|
||||
const secondLocationId = "location_551"
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
reserved_quantity: 5,
|
||||
})
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: secondLocationId,
|
||||
stocked_quantity: 6,
|
||||
reserved_quantity: 1,
|
||||
})
|
||||
|
||||
expect(
|
||||
await inventoryService.adjustInventory(inventoryItem.id, locationId, -5)
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 5,
|
||||
reserved_quantity: 5,
|
||||
incoming_quantity: 0,
|
||||
metadata: null,
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await inventoryService.adjustInventory(
|
||||
inventoryItem.id,
|
||||
secondLocationId,
|
||||
-10
|
||||
)
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: secondLocationId,
|
||||
stocked_quantity: -4,
|
||||
reserved_quantity: 1,
|
||||
incoming_quantity: 0,
|
||||
metadata: null,
|
||||
deleted_at: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,286 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../environment-helpers/use-db")
|
||||
const { simpleProductFactory } = require("../../../factories")
|
||||
const { getContainer } = require("../../../environment-helpers/use-container")
|
||||
const { useExpressServer } = require("../../../environment-helpers/use-api")
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
describe("Inventory Module", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
let invItem1
|
||||
let invItem2
|
||||
let variant1
|
||||
let variant2
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
describe("ProductVariantInventoryService", () => {
|
||||
describe("attachInventoryItem", () => {
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const { variants } = await simpleProductFactory(dbConnection, {
|
||||
variants: [{}, {}],
|
||||
})
|
||||
|
||||
variant1 = variants[0]
|
||||
variant2 = variants[1]
|
||||
|
||||
invItem1 = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-1",
|
||||
})
|
||||
|
||||
invItem2 = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-2",
|
||||
})
|
||||
})
|
||||
|
||||
it("should attach the single item with spread params", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
const variantItems = await pviService.listByVariant(variant1.id)
|
||||
expect(variantItems.length).toEqual(1)
|
||||
expect(variantItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should attach multiple inventory items and variants at once", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
|
||||
const variantItems = await pviService.listByVariant([
|
||||
variant1.id,
|
||||
variant2.id,
|
||||
])
|
||||
expect(variantItems.length).toEqual(2)
|
||||
expect(variantItems).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: variant2.id,
|
||||
inventory_item_id: invItem2.id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should skip existing attachments when attaching a singular inventory item", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
const variantItems = await pviService.listByVariant(variant1.id)
|
||||
expect(variantItems.length).toEqual(1)
|
||||
expect(variantItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should skip existing attachments when attaching multiple inventory items in bulk", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
|
||||
const variantItems = await pviService.listByVariant([
|
||||
variant1.id,
|
||||
variant2.id,
|
||||
])
|
||||
expect(variantItems.length).toEqual(2)
|
||||
expect(variantItems).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: variant2.id,
|
||||
inventory_item_id: invItem2.id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to attach items when a single item has a required_quantity below 1", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id, 0)
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`"requiredQuantity" must be greater than 0, the following entries are invalid: ${JSON.stringify(
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
requiredQuantity: 0,
|
||||
}
|
||||
)}`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
requiredQuantity: 0,
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`"requiredQuantity" must be greater than 0, the following entries are invalid: ${JSON.stringify(
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
requiredQuantity: 0,
|
||||
}
|
||||
)}`
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to attach items when attaching to a non-existing variant", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem("variant1.id", invItem1.id)
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Variants not found for the following ids: variant1.id`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: "variant1.id",
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Variants not found for the following ids: variant1.id`
|
||||
)
|
||||
})
|
||||
it("should fail to attach items when attaching to a non-existing inventory item", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem(variant1.id, "invItem1.id")
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Inventory items not found for the following ids: invItem1.id`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: "invItem2.id",
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Inventory items not found for the following ids: invItem2.id`
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,181 +0,0 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { useApi } from "../../../environment-helpers/use-api"
|
||||
import { AxiosInstance } from "axios"
|
||||
import adminSeeder from "../../../helpers/admin-seeder"
|
||||
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../environment-helpers/use-db")
|
||||
const { getContainer } = require("../../../environment-helpers/use-container")
|
||||
const { useExpressServer } = require("../../../environment-helpers/use-api")
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
describe("List stock locations", () => {
|
||||
let shutdownServer
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let addressId
|
||||
let scId
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const stockLocationModule = appContainer.resolve(
|
||||
ModuleRegistrationName.STOCK_LOCATION
|
||||
)
|
||||
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
const { id: sc_id } = await salesChannelService.create({ name: "test" })
|
||||
scId = sc_id
|
||||
|
||||
const [{ address_id }, { id }] = await Promise.all([
|
||||
stockLocationModule.create({
|
||||
name: "Copenhagen",
|
||||
address: {
|
||||
address_1: "test street 1",
|
||||
country_code: "DK",
|
||||
},
|
||||
}),
|
||||
stockLocationModule.create({
|
||||
name: "Berlin",
|
||||
}),
|
||||
stockLocationModule.create({
|
||||
name: "Paris",
|
||||
}),
|
||||
])
|
||||
|
||||
await salesChannelLocationService.associateLocation(sc_id, id)
|
||||
|
||||
addressId = address_id
|
||||
})
|
||||
|
||||
describe("GET /stock-location", () => {
|
||||
it("should list all stock locations", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const result = await api.get("/admin/stock-locations", adminHeaders)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.stock_locations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "Copenhagen",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "Berlin",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "Paris",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should list stock locations filtered by q parameter", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const result = await api.get(
|
||||
"/admin/stock-locations?q=open",
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.count).toEqual(1)
|
||||
expect(result.data.stock_locations).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "Copenhagen",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should list stock locations filtered by addresses", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const result = await api.get(
|
||||
`/admin/stock-locations?address_id=${addressId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.count).toEqual(1)
|
||||
expect(result.data.stock_locations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "Copenhagen",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should list stock locations filtered by sales channel Id", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const result = await api.get(
|
||||
`/admin/stock-locations?sales_channel_id=${scId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.count).toEqual(1)
|
||||
expect(result.data.stock_locations).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "Berlin",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should list stock locations in requested order", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const nameOrderLocations = [
|
||||
expect.objectContaining({
|
||||
name: "Berlin",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "Copenhagen",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "Paris",
|
||||
}),
|
||||
]
|
||||
|
||||
let result = await api.get(
|
||||
"/admin/stock-locations?order=name",
|
||||
adminHeaders
|
||||
)
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.stock_locations).toEqual(nameOrderLocations)
|
||||
|
||||
result = await api.get("/admin/stock-locations?order=-name", adminHeaders)
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data.stock_locations).toEqual(nameOrderLocations.reverse())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,88 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Sales channels", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Stock Locations", () => {
|
||||
it("When deleting a sales channel, removes all associated locations with it", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
const api = useApi()
|
||||
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "warehouse",
|
||||
})
|
||||
const loc2 = await stockLocationService.create({
|
||||
name: "other place",
|
||||
})
|
||||
|
||||
const sc = await salesChannelService.create({ name: "channel test" })
|
||||
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc.id)
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc2.id)
|
||||
|
||||
expect(await salesChannelService.retrieve(sc.id)).toEqual(
|
||||
expect.objectContaining({
|
||||
id: sc.id,
|
||||
name: "channel test",
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(sc.id)
|
||||
).toHaveLength(2)
|
||||
|
||||
await api.delete(`/admin/sales-channels/${sc.id}`, {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
})
|
||||
|
||||
await expect(salesChannelService.retrieve(sc.id)).rejects.toThrowError()
|
||||
|
||||
await expect(
|
||||
salesChannelLocationService.listLocationIds(sc.id)
|
||||
).rejects.toThrowError()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,96 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Sales channels", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Stock Locations", () => {
|
||||
it("When deleting a stock location, removes all associated sales channels with it", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
const api = useApi()
|
||||
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "warehouse",
|
||||
})
|
||||
|
||||
const saleChannel = await salesChannelService.create({
|
||||
name: "channel test",
|
||||
})
|
||||
|
||||
const otherChannel = await salesChannelService.create({
|
||||
name: "yet another channel",
|
||||
})
|
||||
|
||||
await salesChannelLocationService.associateLocation(
|
||||
saleChannel.id,
|
||||
loc.id
|
||||
)
|
||||
await salesChannelLocationService.associateLocation(
|
||||
otherChannel.id,
|
||||
loc.id
|
||||
)
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(saleChannel.id)
|
||||
).toHaveLength(1)
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(otherChannel.id)
|
||||
).toHaveLength(1)
|
||||
|
||||
await api.delete(`/admin/stock-locations/${loc.id}`, {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
})
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(saleChannel.id)
|
||||
).toHaveLength(0)
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(otherChannel.id)
|
||||
).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,162 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
useApi,
|
||||
useExpressServer,
|
||||
} = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Sales channels", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Stock Locations", () => {
|
||||
describe("CORE", () => {
|
||||
it("When listing a sales channel, it brings all associated locations with it", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const stockLocationService = appContainer.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "warehouse",
|
||||
})
|
||||
const loc2 = await stockLocationService.create({
|
||||
name: "other place",
|
||||
})
|
||||
|
||||
const sc = await salesChannelService.create({ name: "channel test" })
|
||||
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc.id)
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc2.id)
|
||||
|
||||
expect(
|
||||
await salesChannelLocationService.listLocationIds(sc.id)
|
||||
).toHaveLength(2)
|
||||
|
||||
const [channels] = await salesChannelService.listAndCount(
|
||||
{},
|
||||
{
|
||||
relations: ["locations"],
|
||||
}
|
||||
)
|
||||
const createdSC = channels.find((c) => c.id === sc.id)
|
||||
|
||||
expect(channels).toHaveLength(2)
|
||||
expect(createdSC.locations).toHaveLength(2)
|
||||
expect(createdSC).toEqual(
|
||||
expect.objectContaining({
|
||||
id: sc.id,
|
||||
name: "channel test",
|
||||
locations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
sales_channel_id: sc.id,
|
||||
location_id: loc.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sales_channel_id: sc.id,
|
||||
location_id: loc2.id,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("API", () => {
|
||||
it("Filters stock locations based on sales channel ids", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const stockLocationService = appContainer.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
const salesChannelService = appContainer.resolve("salesChannelService")
|
||||
const salesChannelLocationService = appContainer.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "warehouse",
|
||||
})
|
||||
const loc2 = await stockLocationService.create({
|
||||
name: "other place",
|
||||
})
|
||||
|
||||
const sc = await salesChannelService.create({ name: "Default Channel" })
|
||||
const sc2 = await salesChannelService.create({ name: "Physical store" })
|
||||
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc.id)
|
||||
await salesChannelLocationService.associateLocation(sc.id, loc2.id)
|
||||
await salesChannelLocationService.associateLocation(sc2.id, loc2.id)
|
||||
|
||||
const defaultSalesChannelFilterRes = await api.get(
|
||||
`/admin/stock-locations?sales_channel_id=${sc.id}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(defaultSalesChannelFilterRes.data.stock_locations).toHaveLength(
|
||||
2
|
||||
)
|
||||
expect(defaultSalesChannelFilterRes.data.stock_locations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ name: "warehouse" }),
|
||||
expect.objectContaining({ name: "other place" }),
|
||||
])
|
||||
)
|
||||
|
||||
const physicalStoreSalesChannelFilterRes = await api.get(
|
||||
`/admin/stock-locations?sales_channel_id=${sc2.id}`,
|
||||
adminHeaders
|
||||
)
|
||||
expect(
|
||||
physicalStoreSalesChannelFilterRes.data.stock_locations
|
||||
).toHaveLength(1)
|
||||
|
||||
expect(physicalStoreSalesChannelFilterRes.data.stock_locations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ name: "other place" }),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,273 +0,0 @@
|
||||
const path = require("path")
|
||||
|
||||
const {
|
||||
startBootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const {
|
||||
getContainer,
|
||||
} = require("../../../../environment-helpers/use-container")
|
||||
const { useExpressServer } = require("../../../../environment-helpers/use-api")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Stock Location Module", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd })
|
||||
appContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Stock Location Module Interface", () => {
|
||||
it("create", async () => {
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
|
||||
expect(
|
||||
await stockLocationService.create({
|
||||
name: "first location",
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
name: "first location",
|
||||
deleted_at: null,
|
||||
address_id: null,
|
||||
metadata: null,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await stockLocationService.create({
|
||||
name: "second location",
|
||||
metadata: {
|
||||
extra: "abc",
|
||||
},
|
||||
address: {
|
||||
address_1: "addr_1",
|
||||
address_2: "line 2",
|
||||
country_code: "DK",
|
||||
city: "city",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
name: "second location",
|
||||
metadata: {
|
||||
extra: "abc",
|
||||
},
|
||||
address_id: expect.any(String),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("update", async () => {
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "location",
|
||||
address: {
|
||||
address_1: "addr_1",
|
||||
address_2: "line 2",
|
||||
country_code: "DK",
|
||||
city: "city",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
},
|
||||
})
|
||||
const addressId = loc.address_id
|
||||
|
||||
expect(
|
||||
await stockLocationService.retrieve(loc.id, {
|
||||
relations: ["address"],
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: loc.id,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
name: "location",
|
||||
address_id: addressId,
|
||||
metadata: null,
|
||||
address: expect.objectContaining({
|
||||
id: addressId,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
address_1: "addr_1",
|
||||
address_2: "line 2",
|
||||
company: null,
|
||||
city: "city",
|
||||
country_code: "DK",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: { abc: 123 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await stockLocationService.update(loc.id, {
|
||||
name: "location name",
|
||||
address_id: addressId,
|
||||
address: {
|
||||
address_1: "addr_1 updated",
|
||||
country_code: "US",
|
||||
},
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: loc.id,
|
||||
name: "location name",
|
||||
address_id: addressId,
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await stockLocationService.retrieve(loc.id, {
|
||||
relations: ["address"],
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: loc.id,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
name: "location name",
|
||||
address_id: addressId,
|
||||
metadata: null,
|
||||
address: expect.objectContaining({
|
||||
id: addressId,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
address_1: "addr_1 updated",
|
||||
address_2: "line 2",
|
||||
company: null,
|
||||
city: "city",
|
||||
country_code: "US",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: { abc: 123 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("updateAddress", async () => {
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "location",
|
||||
address: {
|
||||
address_1: "addr_1",
|
||||
address_2: "line 2",
|
||||
country_code: "DK",
|
||||
city: "city",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
},
|
||||
})
|
||||
const addressId = loc.address_id
|
||||
|
||||
expect(
|
||||
await stockLocationService.updateAddress(addressId, {
|
||||
address_1: "addr_1 updated",
|
||||
country_code: "US",
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: addressId,
|
||||
address_1: "addr_1 updated",
|
||||
address_2: "line 2",
|
||||
country_code: "US",
|
||||
city: "city",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
await stockLocationService.retrieve(loc.id, {
|
||||
relations: ["address"],
|
||||
})
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
id: loc.id,
|
||||
address_id: addressId,
|
||||
address: expect.objectContaining({
|
||||
id: addressId,
|
||||
address_1: "addr_1 updated",
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("delete", async () => {
|
||||
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||
|
||||
const loc = await stockLocationService.create({
|
||||
name: "location",
|
||||
address: {
|
||||
address_1: "addr_1",
|
||||
address_2: "line 2",
|
||||
country_code: "DK",
|
||||
city: "city",
|
||||
phone: "111222333",
|
||||
province: "province",
|
||||
postal_code: "555-714",
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await stockLocationService.delete(loc.id)
|
||||
|
||||
const deletedItem = stockLocationService.retrieve(loc.id)
|
||||
|
||||
await expect(deletedItem).rejects.toThrow(
|
||||
`StockLocation with id ${loc.id} was not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,96 +0,0 @@
|
||||
import {
|
||||
CreateInventoryItemActions,
|
||||
createInventoryItems,
|
||||
} from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||
|
||||
import { pipe } from "@medusajs/workflows-sdk"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("CreateInventoryItem workflow", function () {
|
||||
let medusaContainer
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd, skipExpressListen: true })
|
||||
medusaContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
it("should compensate all the invoke if something fails", async () => {
|
||||
const workflow = createInventoryItems(medusaContainer)
|
||||
|
||||
workflow.appendAction(
|
||||
"fail_step",
|
||||
CreateInventoryItemActions.createInventoryItems,
|
||||
{
|
||||
invoke: pipe({}, async function failStep() {
|
||||
throw new Error(`Failed`)
|
||||
}),
|
||||
},
|
||||
{
|
||||
noCompensation: true,
|
||||
}
|
||||
)
|
||||
|
||||
const input: WorkflowTypes.InventoryWorkflow.CreateInventoryItemsWorkflowInputDTO =
|
||||
{
|
||||
inventoryItems: [
|
||||
{
|
||||
sku: "TABLE_LEG",
|
||||
description: "Table Leg",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { result, errors, transaction } = await workflow.run({
|
||||
input,
|
||||
context: {},
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
expect(errors).toEqual([
|
||||
{
|
||||
action: "fail_step",
|
||||
handlerType: "invoke",
|
||||
error: expect.objectContaining({ message: `Failed` }),
|
||||
},
|
||||
])
|
||||
|
||||
expect(transaction.getState()).toEqual("reverted")
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].inventoryItem).toEqual(
|
||||
expect.objectContaining({ id: expect.any(String) })
|
||||
)
|
||||
|
||||
const inventoryService: IInventoryService = medusaContainer.resolve(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
const [inventoryItems] = await inventoryService.listInventoryItems(
|
||||
{ id: result[0].inventoryItem.id },
|
||||
{ withDeleted: true }
|
||||
)
|
||||
|
||||
expect(inventoryItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: result[0].inventoryItem.id,
|
||||
deleted_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,138 +0,0 @@
|
||||
import {
|
||||
CreateProductsActions,
|
||||
Handlers,
|
||||
createProducts,
|
||||
} from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, WorkflowTypes } from "@medusajs/types"
|
||||
import { pipe } from "@medusajs/workflows-sdk"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
describe.skip("CreateProduct workflow", function () {
|
||||
let medusaContainer
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd, skipExpressListen: true })
|
||||
medusaContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
it("should compensate all the invoke if something fails", async () => {
|
||||
const workflow = createProducts(medusaContainer)
|
||||
|
||||
workflow.appendAction(
|
||||
"fail_step",
|
||||
CreateProductsActions.attachInventoryItems,
|
||||
{
|
||||
invoke: pipe({}, async function failStep() {
|
||||
throw new Error(`Failed to create products`)
|
||||
}),
|
||||
},
|
||||
{
|
||||
noCompensation: true,
|
||||
}
|
||||
)
|
||||
|
||||
const input: WorkflowTypes.ProductWorkflow.CreateProductsWorkflowInputDTO =
|
||||
{
|
||||
products: [
|
||||
{
|
||||
title: "Test product",
|
||||
type: { value: "physical" },
|
||||
tags: [{ value: "test" }],
|
||||
subtitle: "Test subtitle",
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
prices: [
|
||||
{
|
||||
amount: 100,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Test option",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const manager = medusaContainer.resolve("manager")
|
||||
const context = {
|
||||
manager,
|
||||
}
|
||||
|
||||
const { result, errors, transaction } = await workflow.run({
|
||||
input,
|
||||
context,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
expect(errors).toEqual([
|
||||
{
|
||||
action: "fail_step",
|
||||
handlerType: "invoke",
|
||||
error: expect.objectContaining({
|
||||
message: `Failed to create products`,
|
||||
}),
|
||||
},
|
||||
])
|
||||
|
||||
expect(transaction.getState()).toEqual("reverted")
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
const productId = result[0].id
|
||||
|
||||
let [product] = await Handlers.ProductHandlers.listProducts({
|
||||
container: medusaContainer,
|
||||
context,
|
||||
data: {
|
||||
ids: [productId],
|
||||
},
|
||||
} as any)
|
||||
|
||||
expect(product).toBeUndefined()
|
||||
|
||||
const productModule = medusaContainer.resolve(
|
||||
ModuleRegistrationName.PRODUCT
|
||||
) as IProductModuleService
|
||||
|
||||
;[product] = await productModule.list(
|
||||
{
|
||||
id: productId,
|
||||
},
|
||||
{
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
deleted_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,114 +0,0 @@
|
||||
import {
|
||||
Handlers,
|
||||
updateProducts,
|
||||
UpdateProductsActions,
|
||||
} from "@medusajs/core-flows"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { pipe } from "@medusajs/workflows-sdk"
|
||||
import path from "path"
|
||||
|
||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
import { simpleProductFactory } from "../../../../factories"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
describe.skip("UpdateProduct workflow", function () {
|
||||
let dbConnection
|
||||
let medusaContainer
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
shutdownServer = await startBootstrapApp({ cwd, skipExpressListen: true })
|
||||
medusaContainer = getContainer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await simpleProductFactory(dbConnection, {
|
||||
title: "Original title",
|
||||
id: "to-update",
|
||||
variants: [{ id: "original-variant" }],
|
||||
})
|
||||
})
|
||||
|
||||
it("should compensate all the invoke if something fails", async () => {
|
||||
const workflow = updateProducts(medusaContainer)
|
||||
|
||||
workflow.appendAction(
|
||||
"fail_step",
|
||||
UpdateProductsActions.removeInventoryItems,
|
||||
{
|
||||
invoke: pipe({}, async function failStep() {
|
||||
throw new Error(`Failed to update products`)
|
||||
}),
|
||||
},
|
||||
{
|
||||
noCompensation: true,
|
||||
}
|
||||
)
|
||||
|
||||
const input: WorkflowTypes.ProductWorkflow.UpdateProductsWorkflowInputDTO =
|
||||
{
|
||||
products: [
|
||||
{
|
||||
id: "to-update",
|
||||
title: "Updated title",
|
||||
variants: [
|
||||
{
|
||||
title: "Should be deleted with revert variant",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const manager = medusaContainer.resolve("manager")
|
||||
const context = {
|
||||
manager,
|
||||
}
|
||||
|
||||
const { errors, transaction } = await workflow.run({
|
||||
input,
|
||||
context,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
expect(errors).toEqual([
|
||||
{
|
||||
action: "fail_step",
|
||||
handlerType: "invoke",
|
||||
error: expect.objectContaining({
|
||||
message: `Failed to update products`,
|
||||
}),
|
||||
},
|
||||
])
|
||||
|
||||
expect(transaction.getState()).toEqual("reverted")
|
||||
|
||||
let [product] = await Handlers.ProductHandlers.listProducts({
|
||||
container: medusaContainer,
|
||||
context,
|
||||
data: {
|
||||
ids: ["to-update"],
|
||||
config: { listConfig: { relations: ["variants"] } },
|
||||
},
|
||||
} as any)
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "Original title",
|
||||
id: "to-update",
|
||||
variants: [expect.objectContaining({ id: "original-variant" })],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,100 +0,0 @@
|
||||
const { useApi } = require("../../helpers/use-api")
|
||||
|
||||
const header = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const resolveCall = async (path, payload, header) => {
|
||||
const api = useApi()
|
||||
let res
|
||||
try {
|
||||
const resp = await api.post(path, payload, header)
|
||||
res = resp.status
|
||||
} catch (expectedException) {
|
||||
try {
|
||||
res = expectedException.response.status
|
||||
} catch (_) {
|
||||
console.error(expectedException)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const determineFail = (actual, expected, path) => {
|
||||
if (expected !== actual) {
|
||||
console.log(`failed at path : ${path}`)
|
||||
}
|
||||
expect(actual).toEqual(expected)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to wrap a Call function so that you may reuse some input values.
|
||||
* @param {Function} fun - the function to call with partial information
|
||||
* @param {Object} input - the constant input which we want to supply now
|
||||
* @returns
|
||||
*/
|
||||
module.exports.partial = function (fun, input = {}) {
|
||||
return async (remaining) => await fun({ ...remaining, ...input })
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to assert a specific code result from a POST call.
|
||||
* @param {Object} input - the information needed to make the call
|
||||
* (path & payload) and the expected code (code)
|
||||
*/
|
||||
module.exports.expectPostCallToReturn = async function (
|
||||
input = {
|
||||
code,
|
||||
path,
|
||||
payload: {},
|
||||
}
|
||||
) {
|
||||
const res = await resolveCall(input.path, input.payload, header)
|
||||
determineFail(res, input.code, input.path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to assert a specific code result from multiple POST
|
||||
* calls.
|
||||
* @param {Object} input - the collection of objects to execute
|
||||
* calls from (col), a function which yields the path (pathf),
|
||||
* and another one which provides the payload (payloadf), as
|
||||
* well as the code (code) which we want to assert.
|
||||
*/
|
||||
module.exports.expectAllPostCallsToReturn = async function (
|
||||
input = {
|
||||
code,
|
||||
col,
|
||||
pathf,
|
||||
payloadf,
|
||||
}
|
||||
) {
|
||||
for (const i of input.col) {
|
||||
const res = await resolveCall(
|
||||
input.pathf(i),
|
||||
input.payloadf ? input.payloadf(i) : {},
|
||||
header
|
||||
)
|
||||
determineFail(res, input.code, input.pathf(i))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to retrieve a specific object the response
|
||||
* from get call,
|
||||
* and simultaneously assert that the call was successful.
|
||||
* @param {Object} param0 - contains the path which to
|
||||
* call (path), and the object within the response.data (get)
|
||||
* we want to retrieve.
|
||||
* @returns {Object} found within response.data corresponding
|
||||
* to the get parameter provided.
|
||||
*/
|
||||
module.exports.callGet = async function ({ path, get }) {
|
||||
const api = useApi()
|
||||
const res = await api.get(path, header)
|
||||
|
||||
determineFail(res.status, 200, path)
|
||||
return res?.data[get]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
process.chdir(__dirname)
|
||||
|
||||
module.exports = {
|
||||
name: "Plugins",
|
||||
testEnvironment: `node`,
|
||||
rootDir: "./",
|
||||
testPathIgnorePatterns: [
|
||||
`/examples/`,
|
||||
`/www/`,
|
||||
`/dist/`,
|
||||
`/node_modules/`,
|
||||
`<rootDir>/node_modules/`,
|
||||
`__tests__/fixtures`,
|
||||
`__testfixtures__`,
|
||||
`.cache`,
|
||||
],
|
||||
transformIgnorePatterns: ["/dist", "/node_modules/"],
|
||||
transform: { "^.+\\.[jt]s$": `@swc/jest` },
|
||||
setupFiles: ["../setup-env.js"],
|
||||
setupFilesAfterEnv: ["../setup.js"],
|
||||
globalSetup: "../globalSetup.js",
|
||||
globalTeardown: "../globalTeardown.js",
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
const { Modules } = require("@medusajs/modules-sdk")
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_NAME = process.env.DB_TEMP_NAME
|
||||
const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
|
||||
|
||||
process.env.POSTGRES_URL = DB_URL
|
||||
process.env.LOG_LEVEL = "error"
|
||||
|
||||
const enableMedusaV2 = process.env.MEDUSA_FF_MEDUSA_V2 == "true"
|
||||
|
||||
module.exports = {
|
||||
plugins: [],
|
||||
projectConfig: {
|
||||
// redis_url: REDIS_URL,
|
||||
database_url: DB_URL,
|
||||
database_type: "postgres",
|
||||
jwt_secret: "test",
|
||||
cookie_secret: "test",
|
||||
database_extra: { idle_in_transaction_session_timeout: 0 },
|
||||
},
|
||||
featureFlags: {
|
||||
medusa_v2: enableMedusaV2,
|
||||
},
|
||||
modules: {
|
||||
workflows: true,
|
||||
|
||||
[Modules.AUTH]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/auth",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
name: "emailpass",
|
||||
scopes: {
|
||||
admin: {},
|
||||
store: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
[Modules.STOCK_LOCATION]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/stock-location",
|
||||
},
|
||||
[Modules.INVENTORY]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/inventory",
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/pricing",
|
||||
},
|
||||
[Modules.CACHE]: {
|
||||
resolve: "@medusajs/cache-inmemory",
|
||||
options: { ttl: 0 }, // Cache disabled
|
||||
},
|
||||
[Modules.PRODUCT]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/product",
|
||||
},
|
||||
[Modules.WORKFLOW_ENGINE]: true,
|
||||
},
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "integration-tests-plugins",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test:integration": "jest --silent=false --maxWorkers=50% --bail --detectOpenHandles --clearMocks --forceExit",
|
||||
"build": "babel src -d dist --extensions \".ts,.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/api-key": "workspace:^",
|
||||
"@medusajs/auth": "workspace:*",
|
||||
"@medusajs/cache-inmemory": "workspace:*",
|
||||
"@medusajs/currency": "workspace:^",
|
||||
"@medusajs/customer": "workspace:^",
|
||||
"@medusajs/event-bus-local": "workspace:*",
|
||||
"@medusajs/inventory": "latest",
|
||||
"@medusajs/inventory-next": "workspace:^",
|
||||
"@medusajs/medusa": "workspace:*",
|
||||
"@medusajs/modules-sdk": "workspace:^",
|
||||
"@medusajs/pricing": "workspace:^",
|
||||
"@medusajs/product": "workspace:^",
|
||||
"@medusajs/promotion": "workspace:^",
|
||||
"@medusajs/region": "workspace:^",
|
||||
"@medusajs/stock-location": "latest",
|
||||
"@medusajs/store": "workspace:^",
|
||||
"@medusajs/tax": "workspace:^",
|
||||
"@medusajs/user": "workspace:^",
|
||||
"@medusajs/utils": "workspace:^",
|
||||
"@medusajs/workflow-engine-inmemory": "workspace:*",
|
||||
"faker": "^5.5.3",
|
||||
"medusa-interfaces": "workspace:*",
|
||||
"pg": "^8.11.0",
|
||||
"typeorm": "^0.3.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.12.10",
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/node": "^7.12.10",
|
||||
"@medusajs/types": "workspace:^",
|
||||
"@swc/core": "^1.4.8",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"babel-preset-medusa-package": "*",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-node": "26.6.2"
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { AbstractFileService } from "@medusajs/medusa"
|
||||
import * as fs from "fs"
|
||||
import mkdirp from "mkdirp"
|
||||
import { resolve } from "path"
|
||||
import stream from "stream"
|
||||
|
||||
export default class LocalFileService extends AbstractFileService {
|
||||
constructor({}, options) {
|
||||
super({}, options)
|
||||
this.upload_dir_ =
|
||||
process.env.UPLOAD_DIR ?? options.upload_dir ?? "uploads/images"
|
||||
|
||||
if (!fs.existsSync(this.upload_dir_)) {
|
||||
fs.mkdirSync(this.upload_dir_)
|
||||
}
|
||||
}
|
||||
|
||||
upload(file) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, file.originalname)
|
||||
|
||||
let content = ""
|
||||
if (file.filename) {
|
||||
content = fs.readFileSync(
|
||||
resolve(process.cwd(), "uploads", file.filename)
|
||||
)
|
||||
}
|
||||
|
||||
const pathSegments = path.split("/")
|
||||
pathSegments.splice(-1)
|
||||
const dirname = pathSegments.join("/")
|
||||
mkdirp.sync(dirname, { recursive: true })
|
||||
|
||||
fs.writeFile(path, content.toString(), (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise({ url: path })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
delete({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
fs.unlink(path, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolvePromise("file unlinked")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async getUploadStreamDescriptor({ name, ext }) {
|
||||
const fileKey = `${name}.${ext}`
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
|
||||
const isFileExists = fs.existsSync(path)
|
||||
if (!isFileExists) {
|
||||
await this.upload({ originalname: fileKey })
|
||||
}
|
||||
|
||||
const pass = new stream.PassThrough()
|
||||
pass.pipe(fs.createWriteStream(path))
|
||||
|
||||
return {
|
||||
writeStream: pass,
|
||||
promise: Promise.resolve(),
|
||||
url: `${this.upload_dir_}/${fileKey}`,
|
||||
fileKey,
|
||||
}
|
||||
}
|
||||
|
||||
async getDownloadStream({ fileKey }) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
try {
|
||||
const path = resolve(this.upload_dir_, fileKey)
|
||||
const data = fs.readFileSync(path)
|
||||
const readable = stream.Readable()
|
||||
readable._read = function () {}
|
||||
readable.push(data.toString())
|
||||
readable.push(null)
|
||||
resolvePromise(readable)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getPresignedDownloadUrl({ fileKey }) {
|
||||
return `${this.upload_dir_}/${fileKey}`
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { FulfillmentService } from "medusa-interfaces"
|
||||
|
||||
class TestFulService extends FulfillmentService {
|
||||
static identifier = "test-ful"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
getFulfillmentOptions() {
|
||||
return [
|
||||
{
|
||||
id: "manual-fulfillment",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
return data
|
||||
}
|
||||
|
||||
validateOption(data) {
|
||||
return true
|
||||
}
|
||||
|
||||
canCalculate() {
|
||||
return false
|
||||
}
|
||||
|
||||
calculatePrice() {
|
||||
throw Error("Manual Fulfillment service cannot calculatePrice")
|
||||
}
|
||||
|
||||
createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createReturn() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createFulfillment() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
cancelFulfillment() {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
}
|
||||
|
||||
export default TestFulService
|
||||
@@ -1,19 +0,0 @@
|
||||
import { NotificationService } from "medusa-interfaces"
|
||||
|
||||
class TestNotiService extends NotificationService {
|
||||
static identifier = "test-not"
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
async sendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async resendNotification() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default TestNotiService
|
||||
@@ -1,73 +0,0 @@
|
||||
import { AbstractPaymentService } from "@medusajs/medusa"
|
||||
|
||||
class TestPayService extends AbstractPaymentService {
|
||||
static identifier = "test-pay"
|
||||
|
||||
constructor(_) {
|
||||
super(_)
|
||||
}
|
||||
|
||||
async getStatus(paymentData) {
|
||||
return "authorized"
|
||||
}
|
||||
|
||||
async retrieveSavedMethods(customer) {
|
||||
return []
|
||||
}
|
||||
|
||||
async createPayment(cart) {
|
||||
const fields = [
|
||||
"total",
|
||||
"subtotal",
|
||||
"tax_total",
|
||||
"discount_total",
|
||||
"shipping_total",
|
||||
"gift_card_total",
|
||||
]
|
||||
|
||||
const data = {}
|
||||
for (const k of fields) {
|
||||
data[k] = cart[k]
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
async retrievePayment(data) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getPaymentData(sessionData) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async authorizePayment(sessionData, context = {}) {
|
||||
return { data: {}, status: "authorized" }
|
||||
}
|
||||
|
||||
async updatePaymentData(sessionData, update) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePayment(sessionData, cart) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async deletePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async capturePayment(payment) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async cancelPayment(payment) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default TestPayService
|
||||
@@ -1,231 +0,0 @@
|
||||
import { ProductCategoryRepository } from "@medusajs/medusa/dist/repositories/product-category"
|
||||
import path from "path"
|
||||
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||
import { simpleProductCategoryFactory } from "../../../factories"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Product Categories", () => {
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
describe("Tree Queries (Materialized Paths)", () => {
|
||||
let a1, a11, a111, a12
|
||||
let productCategoryRepository
|
||||
|
||||
beforeEach(async () => {
|
||||
a1 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "a1",
|
||||
is_active: true,
|
||||
rank: 0,
|
||||
})
|
||||
a11 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "a11",
|
||||
parent_category: a1,
|
||||
is_active: true,
|
||||
rank: 0,
|
||||
})
|
||||
a111 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "a111",
|
||||
parent_category: a11,
|
||||
is_active: true,
|
||||
is_internal: true,
|
||||
rank: 0,
|
||||
})
|
||||
a12 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "a12",
|
||||
parent_category: a1,
|
||||
is_active: false,
|
||||
rank: 1,
|
||||
})
|
||||
|
||||
productCategoryRepository = dbConnection.manager.withRepository(
|
||||
ProductCategoryRepository
|
||||
)
|
||||
})
|
||||
|
||||
it("can fetch all root categories", async () => {
|
||||
const rootCategories = await productCategoryRepository.findRoots()
|
||||
|
||||
expect(rootCategories).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "a1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("can fetch all ancestors of a category", async () => {
|
||||
const a11Parent = await productCategoryRepository.findAncestors(a11)
|
||||
|
||||
expect(a11Parent).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "a1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "a11",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("can fetch all root descendants of a category", async () => {
|
||||
const a1Children = await productCategoryRepository.findDescendants(a1)
|
||||
|
||||
expect(a1Children).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "a1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "a11",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "a111",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "a12",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("getFreeTextSearchResultsAndCount", () => {
|
||||
let a1, a11, a111, a12
|
||||
let productCategoryRepository
|
||||
|
||||
beforeEach(async () => {
|
||||
a1 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "skinny jeans",
|
||||
handle: "skinny-jeans",
|
||||
is_active: true,
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
a11 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "winter shirts",
|
||||
handle: "winter-shirts",
|
||||
parent_category: a1,
|
||||
is_active: true,
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
a111 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "running shoes",
|
||||
handle: "running-shoes",
|
||||
parent_category: a11,
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
a12 = await simpleProductCategoryFactory(dbConnection, {
|
||||
name: "casual shoes",
|
||||
handle: "casual-shoes",
|
||||
parent_category: a1,
|
||||
is_internal: true,
|
||||
rank: 1,
|
||||
})
|
||||
|
||||
productCategoryRepository = dbConnection.manager.withRepository(
|
||||
ProductCategoryRepository
|
||||
)
|
||||
})
|
||||
|
||||
it("fetches all active categories", async () => {
|
||||
const [categories, count] =
|
||||
await productCategoryRepository.getFreeTextSearchResultsAndCount({
|
||||
where: { is_active: true },
|
||||
})
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(categories).toEqual([
|
||||
expect.objectContaining({
|
||||
name: a1.name,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: a11.name,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("fetches all internal categories", async () => {
|
||||
const [categories, count] =
|
||||
await productCategoryRepository.getFreeTextSearchResultsAndCount({
|
||||
where: { is_internal: true },
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(categories).toEqual([
|
||||
expect.objectContaining({
|
||||
name: a12.name,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("fetches all categories with query shoes", async () => {
|
||||
const [categories, count] =
|
||||
await productCategoryRepository.getFreeTextSearchResultsAndCount(
|
||||
{ where: {} },
|
||||
"shoes"
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(categories).toEqual([
|
||||
expect.objectContaining({
|
||||
name: a111.name,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: a12.name,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("fetches all categories with query casual-", async () => {
|
||||
const [categories, count] =
|
||||
await productCategoryRepository.getFreeTextSearchResultsAndCount(
|
||||
{ where: {} },
|
||||
"casual-"
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(categories).toEqual([
|
||||
expect.objectContaining({
|
||||
name: a12.name,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("builds relations for categories", async () => {
|
||||
const [categories, count] =
|
||||
await productCategoryRepository.getFreeTextSearchResultsAndCount({
|
||||
where: { id: a11.id },
|
||||
relations: { parent_category: true, category_children: true },
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(categories[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: a11.id,
|
||||
parent_category: expect.objectContaining({
|
||||
id: a1.id,
|
||||
}),
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: a111.id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,21 +0,0 @@
|
||||
module.exports = {
|
||||
name: "repositories",
|
||||
testEnvironment: `node`,
|
||||
rootDir: "./",
|
||||
testTimeout: 10000,
|
||||
testPathIgnorePatterns: [
|
||||
`/examples/`,
|
||||
`/www/`,
|
||||
`/dist/`,
|
||||
`/node_modules/`,
|
||||
`__tests__/fixtures`,
|
||||
`__testfixtures__`,
|
||||
`.cache`,
|
||||
],
|
||||
transformIgnorePatterns: ["/dist", "/node_modules/"],
|
||||
transform: { "^.+\\.[jt]s$": `../../jest-transformer.js` },
|
||||
setupFiles: ["../setup-env.js"],
|
||||
setupFilesAfterEnv: ["../setup.js"],
|
||||
globalSetup: "../globalSetup.js",
|
||||
globalTeardown: "../globalTeardown.js",
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_NAME = process.env.DB_TEMP_NAME
|
||||
|
||||
module.exports = {
|
||||
plugins: [],
|
||||
projectConfig: {
|
||||
redis_url: process.env.REDIS_URL,
|
||||
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`,
|
||||
database_type: "postgres",
|
||||
jwt_secret: "test",
|
||||
cookie_secret: "test",
|
||||
},
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "integration-tests-repositories",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test:integration": "jest --silent=false --runInBand --bail --detectOpenHandles --forceExit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/cache-inmemory": "workspace:*",
|
||||
"@medusajs/event-bus-local": "workspace:*",
|
||||
"@medusajs/medusa": "workspace:*",
|
||||
"medusa-interfaces": "workspace:*",
|
||||
"pg": "^8.11.0",
|
||||
"typeorm": "^0.3.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.12.10",
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/node": "^7.12.10",
|
||||
"babel-preset-medusa-package": "*",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-node": "26.6.2"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user