medusa icon indicating copy to clipboard operation
medusa copied to clipboard

Trouble with end to end test

Open quiloos39 opened this issue 1 year ago • 1 comments

My goal is to construct Medusa environment to able write end to end tests.

Trouble i am having is i am unable to apply migrations to medusa instance and there are couple of problems i am having.

  • I would like spawn fresh postgres instance every time test is ran. In order to achieve that i have to generate database in test environment. Problem with that approach is I am unable to run medusa-cli migrate command since it's actively reading medusa config and I am unable to pass database uri in config at that time.
 FAIL  __test__/stripe-subscriptions.test.ts (5.534 s)
  End to end test for Subscription test
    ✕ 

  ● End to end test for Subscription test › 

    EntityPropertyNotFoundError: Property "default_sales_channel" was not found in "Store". Make sure your query is correct.

      at ../../src/query-builder/SelectQueryBuilder.ts:3911:23
          at Array.forEach (<anonymous>)
      at SelectQueryBuilder.buildRelations (../../src/query-builder/SelectQueryBuilder.ts:3903:32)
      at SelectQueryBuilder.applyFindOptions (../../src/query-builder/SelectQueryBuilder.ts:3103:22)
      at SelectQueryBuilder.setFindOptions (../../src/query-builder/SelectQueryBuilder.ts:105:14)
      at EntityManager.findOne (../../src/entity-manager/EntityManager.ts:1189:14)
      at Repository.findOne (../../src/repository/Repository.ts:577:29)
      at StoreService.<anonymous> (../../node_modules/@medusajs/medusa/src/services/store.ts:91:35)
      at step (../../node_modules/@medusajs/medusa/dist/services/store.js:48:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/services/store.js:29:53)
      at ../../node_modules/@medusajs/medusa/dist/services/store.js:23:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/services/store.js:19:12)
      at StoreService.Object.<anonymous>.StoreService.retrieve (../../node_modules/@medusajs/medusa/dist/services/store.js:173:16)
      at SalesChannelService.<anonymous> (../../node_modules/@medusajs/medusa/src/services/sales-channel.ts:262:10)
      at step (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:59:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:40:53)
      at ../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:34:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:30:12)
      at ../../node_modules/@medusajs/medusa/src/services/sales-channel.ts:259:55
      at SalesChannelService.<anonymous> (../../node_modules/@medusajs/medusa/src/interfaces/transaction-base-service.ts:83:24)
      at step (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:33:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:14:53)
      at ../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:8:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:4:12)
      at doWork (../../node_modules/@medusajs/medusa/src/interfaces/transaction-base-service.ts:79:45)
      at SalesChannelService.<anonymous> (../../node_modules/@medusajs/medusa/src/interfaces/transaction-base-service.ts:97:20)
      at step (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:33:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:14:53)
      at ../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:8:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:4:12)
      at SalesChannelService.Object.<anonymous>.TransactionBaseService.atomicPhase_ (../../node_modules/@medusajs/medusa/dist/interfaces/transaction-base-service.js:81:16)
      at SalesChannelService.<anonymous> (../../node_modules/@medusajs/medusa/src/services/sales-channel.ts:259:17)
      at step (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:59:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:40:53)
      at ../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:34:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:30:12)
      at SalesChannelService.Object.<anonymous>.SalesChannelService.createDefault (../../node_modules/@medusajs/medusa/dist/services/sales-channel.js:361:16)
      at ../../node_modules/@medusajs/medusa/src/loaders/defaults.ts:148:14
      at step (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:33:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:14:53)
      at ../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:8:71
      at Object.<anonymous>.__awaiter (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:4:12)
      at ../../node_modules/@medusajs/medusa/src/loaders/defaults.ts:141:8
      at ../../node_modules/@medusajs/medusa/src/loaders/defaults.ts:152:9
      at step (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:33:23)
      at Object.next (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:14:53)
      at fulfilled (../../node_modules/@medusajs/medusa/dist/loaders/defaults.js:5:58)
// stripe-subscriptions.test.ts
import { MedusaContainer } from "@medusajs/medusa";
import * as http from "http";
import { PostgreSqlContainer, StartedPostgreSqlContainer } from "testcontainers";
import { DataSource } from "typeorm";
import { medusaInitialize } from "../src/lib/spawn-medusa";

describe("End to end test for Subscription test", () => {
  let dbConnection: DataSource;
  let httpServer: http.Server;
  let container: MedusaContainer;
  let dbContainer: StartedPostgreSqlContainer;

  beforeAll(async () => {
    dbContainer = await new PostgreSqlContainer()
      //
      .withUsername("medusa")
      .withPassword("medusa")
      .withDatabase("medusa")
      .start();

    const medusa = await medusaInitialize({
      databaseUrl: dbContainer.getConnectionUri(),
    });

    dbConnection = medusa.dbConnection;
    httpServer = medusa.httpServer;
    container = medusa.container;
    
  }, 30000);

  afterAll(async () => {
    if (dbConnection?.isInitialized) {
      await dbConnection.destroy();
    }
    if (httpServer?.listening) {
      await new Promise((resolve) => httpServer.close(resolve));
    }
    await dbContainer.stop();
  });

  it("Test", async () => {});
});
// spawn-medusa.ts
import apiLoader from "@medusajs/medusa/dist/loaders/api";
import databaseLoader, { dataSource } from "@medusajs/medusa/dist/loaders/database";
import defaultsLoader from "@medusajs/medusa/dist/loaders/defaults";
import expressLoader from "@medusajs/medusa/dist/loaders/express";
import featureFlagsLoader from "@medusajs/medusa/dist/loaders/feature-flags";
import Logger from "@medusajs/medusa/dist/loaders/logger";
import modelsLoader from "@medusajs/medusa/dist/loaders/models";
import passportLoader from "@medusajs/medusa/dist/loaders/passport";
import pluginsLoader, { registerPluginModels } from "@medusajs/medusa/dist/loaders/plugins";
import redisLoader from "@medusajs/medusa/dist/loaders/redis";
import repositoriesLoader from "@medusajs/medusa/dist/loaders/repositories";
import searchIndexLoader from "@medusajs/medusa/dist/loaders/search-index";
import servicesLoader from "@medusajs/medusa/dist/loaders/services";
import strategiesLoader from "@medusajs/medusa/dist/loaders/strategies";
import subscribersLoader from "@medusajs/medusa/dist/loaders/subscribers";
import { asValue } from "awilix";
import express, { Express, NextFunction, Request, Response } from "express";
import "reflect-metadata";

import { ConfigModule } from "@medusajs/medusa";
import { moduleLoader, registerModules } from "@medusajs/modules-sdk";
import axios from "axios";
import { createMedusaContainer } from "medusa-core-utils";

export const medusaInitialize = async ({ databaseUrl }: { databaseUrl: string }) => {
  const PORT = 9000;
  const rootDirectory = process.cwd();
  const app: Express = express();

  // To mute the logger i override the logger with a dummy logger
  const logger = {
    info: () => null,
    error: () => null,
    warn: () => null,
    progress: () => null,
  } as Logger;

  type ExtendedConfigModule = ConfigModule & {
    projectConfig: {
      stripe: {
        success_url: string;
        secret_key: string;
        webhook_secret: string;
      };
    };
  };

  const configModule: ExtendedConfigModule = {
    featureFlags: {
      product_categories: true,
    },
    projectConfig: {
      database_type: "postgres",
      database_url: databaseUrl,
      database_logging: false,
      store_cors: "/.*/",
      admin_cors: "/.*/",
      jwt_secret: "SECRET",
      cookie_secret: "SECRET",
      stripe: {
        success_url: process.env.STRIPE_SUCCESS_URL,
        secret_key: process.env.STRIPE_SECRET_KEY,
        webhook_secret: process.env.STRIPE_WEBHOOK_SECRET,
      },
    },
    modules: {
      cacheService: {
        resolve: "@medusajs/cache-inmemory",
        options: {
          ttl: 30,
        },
      },
      eventBus: {
        resolve: "@medusajs/event-bus-local",
      },
    },
    plugins: [],
  };

  const container = createMedusaContainer();
  container.register("configModule", asValue(configModule));

  const featureFlagRouter = featureFlagsLoader(configModule, logger);
  container.register({
    logger: asValue(logger),
    featureFlagRouter: asValue(featureFlagRouter),
  });

  await redisLoader({ container, configModule, logger });

  modelsLoader({ container });

  await registerPluginModels({
    rootDirectory,
    container,
    configModule,
  });

  strategiesLoader({ container, configModule, isTest: false });

  await moduleLoader({
    container,
    moduleResolutions: registerModules(configModule?.modules),
    logger: logger,
  });

  const dbConnection = await databaseLoader({
    container,
    configModule,
    customOptions: {
      migrations: ["/home/quiloos39/Documents/work/traumkaffee/node_modules/@medusajs/medusa/dist/migrations/*.js"],
      logging: false,
    },
  });

  await dbConnection.runMigrations();

  repositoriesLoader({ container });

  container.register({ manager: asValue(dataSource.manager) });

  servicesLoader({ container, configModule, isTest: false });

  await expressLoader({ app: app, configModule });
  await passportLoader({ app: app, container, configModule });

  // Add the registered services to the request scope
  app.use((req: Request, res: Response, next: NextFunction) => {
    container.register({ manager: asValue(dataSource.manager) });
    (req as any).scope = container.createScope();
    next();
  });

  console.log("Loading plugins");

  await pluginsLoader({
    container,
    rootDirectory,
    configModule,
    app: app,
    activityId: null,
  });

  console.log("Loading subscribers");

  subscribersLoader({ container });

  console.log("Loading api");
  await apiLoader({ container, app: app, configModule });

  console.log("Loading defaults");
  await defaultsLoader({ container });

  console.log("Loading search index");
  await searchIndexLoader({ container });

  const httpServer = app.listen(PORT);

  // Awaiting until server is ready
  await new Promise(async (resolve) => {
    let signal: NodeJS.Timeout;
    signal = setInterval(async () => {
      const response = await axios.get(`http://localhost:${PORT}/health`).catch((e) => null);
      if (response) {
        clearTimeout(signal);
        resolve(true);
      }
    }, 1000);
  });

  console.log("Done loading");

  return {
    container,
    dbConnection,
    httpServer,
  };
};

This is part where i apply migrations it's hard coded path for now but will be changed.

  const dbConnection = await databaseLoader({
    container,
    configModule,
    customOptions: {
      migrations: ["/home/quiloos39/Documents/work/traumkaffee/node_modules/@medusajs/medusa/dist/migrations/*.js"],
      logging: false,
    },
  });

quiloos39 avatar Jul 10 '23 08:07 quiloos39

did you manage to solve that issue?

vholik avatar Feb 05 '24 11:02 vholik

Any updates?

BartekCK avatar May 21 '24 13:05 BartekCK

Hey, thanks for the report! Since v2 brought a lot of architectural and API changes on the backend, we will be closing this ticket since it no longer applies to our new setup, or the issue has already been fixed. If you are still facing issues with v1, please open a new ticket and we will address it as soon as possible. Thanks! 🙏

sradevski avatar Jul 05 '24 10:07 sradevski