import "reflect-metadata" import { createTestingConnections, closeTestingConnections, reloadTestingDatabases, } from "../../utils/test-utils" import { DataSource } from "../../../src/data-source/index" import { expect } from "chai" import { AnimalEntity } from "./entity/Animal" import { CatEntity } from "./entity/Cat" import { DogEntity } from "./entity/Dog" import { NnaryOperator } from "./entity/NnaryOperator" import { NumberEntry } from "./entity/NumberEntry" import { OperatorTreeEntry } from "./entity/OperatorTreeEntry" import { PersonEntity } from "./entity/Person" import { Photo } from "./entity/Photo" describe("github issues > #7558 Child entities' wrong discriminator value when embedded in parent entity", () => { let dataSources: DataSource[] before( async () => (dataSources = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], schemaCreate: true, dropSchema: true, })), ) beforeEach(() => reloadTestingDatabases(dataSources)) after(() => closeTestingConnections(dataSources)) it("should use the correct subclass for inheritance when saving & retrieving a single STI entity (one-to-one)", () => Promise.all( dataSources.map(async (dataSource) => { const entityManager = dataSource.createEntityManager() const personRepo = entityManager.getRepository(PersonEntity) const photoRepo = entityManager.getRepository(Photo) const photo = photoRepo.create({ title: "Cat Photo", size: 42, }) expect(photo).to.be.instanceOf(Photo) // Create a new person, cascade saving the content. const person = personRepo.create({ pets: [], content: photo, }) expect(person.content).not.to.be.undefined expect(person.content).to.be.instanceOf(Photo) await entityManager.save(person) // Retrieve it back from the DB. const persons = await personRepo.find() expect(persons.length).to.equal(1) // And check whether the content / photo is still the same. expect(persons[0].pets.length).to.equal(0) expect(persons[0].content).not.to.be.undefined expect(persons[0].content).not.to.be.null expect(persons[0].content).to.be.instanceOf(Photo) expect(persons[0].content).to.include(photo) }), )) it("should use the correct subclass for inheritance when saving & retrieving multiple STI entities (one-to-many)", () => Promise.all( dataSources.map(async (dataSource) => { const entityManager = dataSource.createEntityManager() const personRepo = entityManager.getRepository(PersonEntity) const animalRepo = entityManager.getRepository(AnimalEntity) const dogRepo = entityManager.getRepository(DogEntity) const catRepo = entityManager.getRepository(CatEntity) const mysteryUnicorn = animalRepo.create({ name: "i-am-a-mystery", }) expect(mysteryUnicorn).to.be.instanceOf(AnimalEntity) const felix = catRepo.create({ name: "felix", livesLeft: 9, // Cat stuff }) expect(felix).to.be.instanceOf(CatEntity) const spike = dogRepo.create({ name: "spike", steaksEaten: 42, // Dog stuff }) expect(spike).to.be.instanceOf(DogEntity) const pets = [mysteryUnicorn, felix, spike] // Create a new person, cascade saving the pets. const person = personRepo.create({ pets, }) expect(person.pets[0]).to.be.instanceOf(AnimalEntity) expect(person.pets[1]).to.be.instanceOf(CatEntity) expect(person.pets[2]).to.be.instanceOf(DogEntity) await entityManager.save(person) // Retrieve it back from the DB. const persons = await personRepo.find({ relations: ["pets"] }) expect(persons.length).to.equal(1) // And check whether the pets are still the same. expect(persons[0].pets.length).to.equal(3) const animalPets = persons[0].pets.filter( (entity) => entity instanceof AnimalEntity && !(entity instanceof CatEntity) && !(entity instanceof DogEntity), ) const catPets = persons[0].pets.filter( (entity) => entity instanceof CatEntity, ) const dogPets = persons[0].pets.filter( (entity) => entity instanceof DogEntity, ) expect(animalPets.length).to.equal(1) expect(animalPets[0]).to.include(mysteryUnicorn) expect(catPets.length).to.equal(1) expect(catPets[0]).to.include(felix) expect(dogPets.length).to.equal(1) expect(dogPets[0]).to.include(spike) }), )) it("should set the correct discriminators for trees in STI settings", () => Promise.all( dataSources.map(async (dataSource) => { const entityManager = dataSource.createEntityManager() const operatorTreeRepo = entityManager.getTreeRepository(OperatorTreeEntry) const nnaryOperatorRepo = entityManager.getTreeRepository(NnaryOperator) const numberEntryRepo = entityManager.getRepository(NumberEntry) // Create an operator tree (10.5 * (21 + 42)). const num1 = numberEntryRepo.create({ value: 42 }) const num2 = numberEntryRepo.create({ value: 21 }) const num3 = numberEntryRepo.create({ value: 10.5 }) const mulOperator = nnaryOperatorRepo.create({ operator: "*", }) // Save the tree... // We need to first save the parents step by step. const savedMulOperator = await operatorTreeRepo.save( mulOperator, ) const plusOperator = nnaryOperatorRepo.create({ parent: savedMulOperator, operator: "+", }) num3.parent = savedMulOperator await operatorTreeRepo.save(num3) const savedPlusOperator = await operatorTreeRepo.save( plusOperator, ) num1.parent = savedPlusOperator num2.parent = savedPlusOperator await operatorTreeRepo.save([num1, num2]) // ... and fetch it from the database. const fetchedTrees = await operatorTreeRepo.findTrees() expect(fetchedTrees.length).to.eql(1) expect(fetchedTrees[0]).to.be.instanceOf(NnaryOperator) const fetchedOperator = fetchedTrees[0] as NnaryOperator expect(fetchedOperator.operator).to.eql("*") expect(fetchedOperator.children.length).to.be.eql(2) // Order is *not* deterministic by default... expect( fetchedOperator.children.find( (operator) => operator.id === num3.id, ), ).to.be.instanceOf(NumberEntry) expect( fetchedOperator.children.find( (operator) => operator.id === savedPlusOperator.id, ), ).to.be.instanceOf(NnaryOperator) const subOperator = fetchedOperator.children.find( (operator) => operator.id === savedPlusOperator.id, ) as NnaryOperator expect(subOperator.operator).to.eql("+") expect(subOperator.children.length).to.be.eql(2) expect(subOperator.children[0]).to.be.instanceOf(NumberEntry) expect(subOperator.children[1]).to.be.instanceOf(NumberEntry) }), )) })