import "reflect-metadata" import { expect } from "chai" import { DataSource } from "../../src/data-source/DataSource" import { Repository } from "../../src/repository/Repository" import { PostDetails } from "../../sample/sample2-one-to-one/entity/PostDetails" import { Post } from "../../sample/sample2-one-to-one/entity/Post" import { PostCategory } from "../../sample/sample2-one-to-one/entity/PostCategory" import { PostAuthor } from "../../sample/sample2-one-to-one/entity/PostAuthor" import { PostMetadata } from "../../sample/sample2-one-to-one/entity/PostMetadata" import { PostImage } from "../../sample/sample2-one-to-one/entity/PostImage" import { PostInformation } from "../../sample/sample2-one-to-one/entity/PostInformation" import { setupSingleTestingConnection } from "../utils/test-utils" describe("one-to-one", function () { // ------------------------------------------------------------------------- // Configuration // ------------------------------------------------------------------------- // connect to db let dataSource: DataSource before(async function () { const options = setupSingleTestingConnection("mysql", { entities: [ Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor, ], }) if (!options) return dataSource = new DataSource(options) await dataSource.initialize() }) after(() => dataSource.destroy()) // clean up database before each test function reloadDatabase() { if (!dataSource) return return dataSource.synchronize(true) } let postRepository: Repository, postDetailsRepository: Repository, postCategoryRepository: Repository, postImageRepository: Repository, postMetadataRepository: Repository before(function () { if (!dataSource) return postRepository = dataSource.getRepository(Post) postDetailsRepository = dataSource.getRepository(PostDetails) postCategoryRepository = dataSource.getRepository(PostCategory) postImageRepository = dataSource.getRepository(PostImage) postMetadataRepository = dataSource.getRepository(PostMetadata) }) // ------------------------------------------------------------------------- // Specifications // ------------------------------------------------------------------------- describe("insert post and details (has inverse relation + full cascade options)", function () { if (!dataSource) return let newPost: Post, details: PostDetails, savedPost: Post before(reloadDatabase) before(function () { details = new PostDetails() details.authorName = "Umed" details.comment = "this is post" details.metadata = "post,posting,postman" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" newPost.details = details return postRepository .save(newPost) .then((post) => (savedPost = post as Post)) }) it("should return the same post instance after its created", function () { savedPost.should.be.equal(newPost) }) it("should return the same post details instance after its created", function () { savedPost.details!.should.be.equal(newPost.details) }) it("should have a new generated id after post is created", function () { expect(savedPost.id).not.to.be.undefined expect(savedPost.details!.id).not.to.be.undefined }) it("should have inserted post in the database", function () { if (!dataSource) return const expectedPost = new Post() expectedPost.id = savedPost.id expectedPost.text = savedPost.text expectedPost.title = savedPost.title return postRepository .findOneBy({ id: savedPost.id, }) .should.eventually.eql(expectedPost) }) it("should have inserted post details in the database", async function () { if (!dataSource) return const expectedDetails = new PostDetails() expectedDetails.id = savedPost.details!.id expectedDetails.authorName = savedPost.details!.authorName expectedDetails.comment = savedPost.details!.comment expectedDetails.metadata = savedPost.details!.metadata const loadedPostDetails = await postDetailsRepository.findOneBy({ id: savedPost.details!.id, }) loadedPostDetails!.should.be.eql(expectedDetails) }) it("should load post and its details if left join used", async function () { if (!dataSource) return const expectedPost = new Post() expectedPost.id = savedPost.id expectedPost.text = savedPost.text expectedPost.title = savedPost.title expectedPost.details = new PostDetails() expectedPost.details!.id = savedPost.details!.id expectedPost.details!.authorName = savedPost.details!.authorName expectedPost.details!.comment = savedPost.details!.comment expectedPost.details!.metadata = savedPost.details!.metadata const post = await postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.details", "details") .where("post.id=:id") .setParameter("id", savedPost.id) .getOne() expect(post).not.to.be.null post!.should.eql(expectedPost) }) it("should load details and its post if left join used (from reverse side)", function () { if (!dataSource) return const expectedDetails = new PostDetails() expectedDetails.id = savedPost.details!.id expectedDetails.authorName = savedPost.details!.authorName expectedDetails.comment = savedPost.details!.comment expectedDetails.metadata = savedPost.details!.metadata expectedDetails.post = new Post() expectedDetails.post.id = savedPost.id expectedDetails.post.text = savedPost.text expectedDetails.post.title = savedPost.title return postDetailsRepository .createQueryBuilder("details") .leftJoinAndSelect("details.post", "post") .where("details.id=:id") .setParameter("id", savedPost.id) .getOne() .should.eventually.eql(expectedDetails) }) it("should load saved post without details if left joins are not specified", function () { const expectedPost = new Post() expectedPost.id = savedPost.id expectedPost.text = savedPost.text expectedPost.title = savedPost.title return postRepository .createQueryBuilder("post") .where("post.id=:id", { id: savedPost.id }) .getOne() .should.eventually.eql(expectedPost) }) it("should load saved post without details if left joins are not specified", function () { const expectedDetails = new PostDetails() expectedDetails.id = savedPost.details!.id expectedDetails.authorName = savedPost.details!.authorName expectedDetails.comment = savedPost.details!.comment expectedDetails.metadata = savedPost.details!.metadata return postDetailsRepository .createQueryBuilder("details") .where("details.id=:id", { id: savedPost.id }) .getOne() .should.eventually.eql(expectedDetails) }) }) describe("insert post and category (one-side relation)", function () { if (!dataSource) return let newPost: Post, category: PostCategory, savedPost: Post before(reloadDatabase) before(function () { category = new PostCategory() category.name = "technology" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" newPost.category = category return postRepository .save(newPost) .then((post) => (savedPost = post as Post)) }) it("should return the same post instance after its created", function () { savedPost.should.be.equal(newPost) }) it("should return the same post category instance after its created", function () { savedPost.category.should.be.equal(newPost.category) }) it("should have a new generated id after post is created", function () { expect(savedPost.id).not.to.be.undefined expect(savedPost.category.id).not.to.be.undefined }) it("should have inserted post in the database", function () { const expectedPost = new Post() expectedPost.id = savedPost.id expectedPost.text = savedPost.text expectedPost.title = savedPost.title return postRepository .findOneBy({ id: savedPost.id, }) .should.eventually.eql(expectedPost) }) it("should have inserted category in the database", function () { const expectedPost = new PostCategory() expectedPost.id = savedPost.category.id expectedPost.name = "technology" return postCategoryRepository .findOneBy({ id: savedPost.category.id, }) .should.eventually.eql(expectedPost) }) it("should load post and its category if left join used", function () { const expectedPost = new Post() expectedPost.id = savedPost.id expectedPost.title = savedPost.title expectedPost.text = savedPost.text expectedPost.category = new PostCategory() expectedPost.category.id = savedPost.category.id expectedPost.category.name = savedPost.category.name return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.category", "category") .where("post.id=:id", { id: savedPost.id }) .getOne() .should.eventually.eql(expectedPost) }) it("should load details and its post if left join used (from reverse side)", function () { // later need to specify with what exception we reject it /*return postCategoryRepository .createQueryBuilder("category") .leftJoinAndSelect("category.post", "post") .where("category.id=:id", { id: savedPost.id }) .getSingleResult() .should.be.rejectedWith(Error);*/ // not working, find fix }) }) describe("cascade updates should not be executed when cascadeUpdate option is not set", function () { if (!dataSource) return let newPost: Post, details: PostDetails before(reloadDatabase) before(function () { details = new PostDetails() details.authorName = "Umed" details.comment = "this is post" details.metadata = "post,posting,postman" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" newPost.details = details return postRepository.save(newPost) }) it("should ignore updates in the model and do not update the db when entity is updated", function () { newPost.details!.comment = "i am updated comment" return postRepository .save(newPost) .then((updatedPost) => { updatedPost.details!.comment!.should.be.equal( "i am updated comment", ) return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.details", "details") .where("post.id=:id") .setParameter("id", updatedPost.id) .getOne() }) .then((updatedPostReloaded) => { updatedPostReloaded!.details!.comment.should.be.equal( "this is post", ) }) }) // todo: also check that updates throw exception in strict cascades mode }) describe("cascade remove should not be executed when cascadeRemove option is not set", function () { if (!dataSource) return let newPost: Post, details: PostDetails before(reloadDatabase) before(function () { details = new PostDetails() details.authorName = "Umed" details.comment = "this is post" details.metadata = "post,posting,postman" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" newPost.details = details return postRepository.save(newPost) }) it("should ignore updates in the model and do not update the db when entity is updated", function () { if (!dataSource) return delete newPost.details return postRepository .save(newPost) .then((updatedPost) => { return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.details", "details") .where("post.id=:id") .setParameter("id", updatedPost.id) .getOne() }) .then((updatedPostReloaded) => { updatedPostReloaded!.details!.comment.should.be.equal( "this is post", ) }) }) }) // todo: check why it generates extra query describe("cascade updates should be executed when cascadeUpdate option is set", function () { if (!dataSource) return let newPost: Post, newImage: PostImage before(reloadDatabase) it("should update a relation successfully when updated", function () { newImage = new PostImage() newImage.url = "logo.png" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" return postImageRepository .save(newImage) .then((image) => { newPost.image = image as PostImage return postRepository.save(newPost) }) .then((post) => { newPost = post as Post return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.image", "image") .where("post.id=:id") .setParameter("id", post.id) .getOne() }) .then((loadedPost) => { loadedPost!.image.url = "new-logo.png" return postRepository.save(loadedPost!) }) .then(() => { return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.image", "image") .where("post.id=:id") .setParameter("id", newPost.id) .getOne() }) .then((reloadedPost) => { reloadedPost!.image.url.should.be.equal("new-logo.png") }) }) }) describe("cascade remove should be executed when cascadeRemove option is set", function () { if (!dataSource) return let newPost: Post, newMetadata: PostMetadata before(reloadDatabase) it("should remove a relation entity successfully when removed", function () { newMetadata = new PostMetadata() newMetadata.description = "this is post metadata" newPost = new Post() newPost.text = "Hello post" newPost.title = "this is post title" return postMetadataRepository .save(newMetadata) .then((metadata) => { newPost.metadata = metadata as PostMetadata return postRepository.save(newPost) }) .then((post) => { newPost = post as Post return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.metadata", "metadata") .where("post.id=:id") .setParameter("id", post.id) .getOne() }) .then((loadedPost) => { loadedPost!.metadata = null return postRepository.save(loadedPost!) }) .then(() => { return postRepository .createQueryBuilder("post") .leftJoinAndSelect("post.metadata", "metadata") .where("post.id=:id") .setParameter("id", newPost.id) .getOne() }) .then((reloadedPost) => { expect(reloadedPost!.metadata).to.not.exist }) }) }) })