import { test } from '@japa/runner'
import {
  DomainAssociation,
  AmfShapes,
  DomainAssociationKind,
  Thing,
  AssociationBinding,
  DataDomain,
  DataFormat,
  SemanticType,
  type OnDeleteRule,
} from '../../../src/index.js'

test.group('DomainAssociation.createSchema()', () => {
  test('creates a valid schema with default values', ({ assert }) => {
    const schema = DomainAssociation.createSchema()
    assert.equal(schema.kind, DomainAssociationKind)
    assert.typeOf(schema.key, 'string')
    assert.isDefined(schema.info)
    assert.deepEqual(schema.info, Thing.fromJSON({ name: 'new_association' }).toJSON())
    assert.isUndefined(schema.schema)
    assert.isUndefined(schema.multiple)
    assert.isUndefined(schema.required)
    assert.isUndefined(schema.bindings)
    assert.isUndefined(schema.targets)
    assert.deepEqual(schema.semantics, [])
    assert.isUndefined(schema.onDelete)
    assert.isUndefined(schema.readOnly)
  })

  test('creates a schema with provided key', ({ assert }) => {
    const key = 'test-key'
    const schema = DomainAssociation.createSchema({ key })
    assert.equal(schema.key, key)
  })

  test('creates a schema with provided info', ({ assert }) => {
    const info = { name: 'Test Association', description: 'Test description' }
    const schema = DomainAssociation.createSchema({ info })
    assert.deepEqual(schema.info, Thing.fromJSON(info).toJSON())
  })

  test('creates a schema with provided semantics', ({ assert }) => {
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const schema = DomainAssociation.createSchema({ semantics })
    assert.deepEqual(schema.semantics, semantics)
  })

  test('creates a schema with provided onDelete rule', ({ assert }) => {
    const onDelete: OnDeleteRule = 'cascade'
    const schema = DomainAssociation.createSchema({ onDelete })
    assert.equal(schema.onDelete, onDelete)
  })

  test('creates a schema with provided readOnly rule', ({ assert }) => {
    const schema = DomainAssociation.createSchema({ readOnly: true })
    assert.isTrue(schema.readOnly)
  })

  test('creates a schema with provided schema', ({ assert }) => {
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const schema = DomainAssociation.createSchema({ schema: schemaDefinition })
    assert.deepEqual(schema.schema, schemaDefinition)
  })

  test('creates a schema with provided multiple', ({ assert }) => {
    const schema = DomainAssociation.createSchema({ multiple: true })
    assert.isTrue(schema.multiple)
  })

  test('creates a schema with provided required', ({ assert }) => {
    const schema = DomainAssociation.createSchema({ required: true })
    assert.isTrue(schema.required)
  })

  test('creates a schema with provided bindings', ({ assert }) => {
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const schema = DomainAssociation.createSchema({ bindings })
    assert.deepEqual(schema.bindings, bindings)
  })

  test('creates a schema with provided targets', ({ assert }) => {
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const schema = DomainAssociation.createSchema({ targets })
    assert.deepEqual(schema.targets, targets)
  })

  test('creates a schema with all properties', ({ assert }) => {
    const key = 'test-key'
    const info = { name: 'Test Association', description: 'Test description' }
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const multiple = true
    const required = true
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: false } }]
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const readOnly = true
    const onDelete: OnDeleteRule = 'restrict'

    const schema = DomainAssociation.createSchema({
      key,
      info,
      schema: schemaDefinition,
      multiple,
      required,
      bindings,
      targets,
      semantics,
      onDelete,
      readOnly,
    })

    assert.equal(schema.key, key)
    assert.deepEqual(schema.info, Thing.fromJSON(info).toJSON())
    assert.deepEqual(schema.schema, schemaDefinition)
    assert.equal(schema.multiple, multiple)
    assert.equal(schema.required, required)
    assert.deepEqual(schema.bindings, bindings)
    assert.deepEqual(schema.targets, targets)
    assert.deepEqual(schema.semantics, semantics)
    assert.equal(schema.onDelete, onDelete)
    assert.isTrue(schema.readOnly)
  })

  test('creates a schema with cloned bindings', ({ assert }) => {
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const schema = DomainAssociation.createSchema({ bindings })
    assert.deepEqual(schema.bindings, bindings)
    assert.notStrictEqual(schema.bindings, bindings)
  })

  test('creates a schema with cloned semantics', ({ assert }) => {
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const schema = DomainAssociation.createSchema({ semantics })
    assert.deepEqual(schema.semantics, semantics)
    assert.notStrictEqual(schema.semantics, semantics)
  })

  test('creates a schema with cloned targets', ({ assert }) => {
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const schema = DomainAssociation.createSchema({ targets })
    assert.deepEqual(schema.targets, targets)
    assert.notStrictEqual(schema.targets, targets)
  })

  test('creates a schema with cloned schema', ({ assert }) => {
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const schema = DomainAssociation.createSchema({ schema: schemaDefinition })
    assert.deepEqual(schema.schema, schemaDefinition)
    assert.notStrictEqual(schema.schema, schemaDefinition)
  })
})

test.group('DomainAssociation.constructor()', () => {
  test('creates a valid instance with default values', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    assert.equal(association.kind, DomainAssociationKind)
    assert.typeOf(association.key, 'string')
    assert.deepEqual(association.info.toJSON(), Thing.fromJSON({ name: 'new_association' }).toJSON())
    assert.isUndefined(association.schema)
    assert.isUndefined(association.multiple)
    assert.isUndefined(association.required)
    assert.deepEqual(association.bindings, [])
    assert.deepEqual(association.targets, [])
    assert.deepEqual(association.semantics, [])
    assert.isUndefined(association.onDelete)
    assert.isUndefined(association.readOnly)
  })

  test('creates an instance with provided key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const key = 'test-key'
    const association = new DomainAssociation(dataDomain, parentKey, { key })
    assert.equal(association.key, key)
  })

  test('creates an instance with provided semantics', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const association = new DomainAssociation(dataDomain, parentKey, { semantics })
    assert.deepEqual(association.semantics, semantics)
  })

  test('creates an instance with provided onDelete rule', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const onDelete: OnDeleteRule = 'setNull'
    const association = new DomainAssociation(dataDomain, parentKey, { onDelete })
    assert.equal(association.onDelete, onDelete)
  })

  test('creates an instance with provided readOnly rule', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const readOnly = true
    const association = new DomainAssociation(dataDomain, parentKey, { readOnly })
    assert.isTrue(association.readOnly)
  })

  test('creates an instance with provided info', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const info = { name: 'Test Association', description: 'Test description' }
    const association = new DomainAssociation(dataDomain, parentKey, { info })
    assert.deepEqual(association.info.toJSON(), Thing.fromJSON(info).toJSON())
  })

  test('creates an instance with provided schema', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, parentKey, { schema: schemaDefinition })
    assert.deepEqual(association.schema, schemaDefinition)
  })

  test('creates an instance with provided multiple', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey, { multiple: true })
    assert.isTrue(association.multiple)
  })

  test('creates an instance with provided required', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey, { required: true })
    assert.isTrue(association.required)
  })

  test('creates an instance with provided bindings', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const association = new DomainAssociation(dataDomain, parentKey, { bindings })
    assert.deepEqual(association.bindings, bindings)
  })

  test('creates an instance with provided targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const association = new DomainAssociation(dataDomain, parentKey, { targets })
    assert.deepEqual(association.targets, targets)
  })

  test('creates an instance with all properties', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const key = 'test-key'
    const info = { name: 'Test Association', description: 'Test description' }
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const multiple = true
    const required = true
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: false } }]
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const readOnly = false
    const onDelete: OnDeleteRule = 'cascade'

    const association = new DomainAssociation(dataDomain, parentKey, {
      key,
      info,
      schema: schemaDefinition,
      multiple,
      required,
      bindings,
      targets,
      semantics,
      onDelete,
      readOnly,
    })

    assert.equal(association.key, key)
    assert.deepEqual(association.info.toJSON(), Thing.fromJSON(info).toJSON())
    assert.deepEqual(association.schema, schemaDefinition)
    assert.equal(association.multiple, multiple)
    assert.equal(association.required, required)
    assert.deepEqual(association.bindings, bindings)
    assert.deepEqual(association.targets, targets)
    assert.deepEqual(association.semantics, semantics)
    assert.equal(association.onDelete, onDelete)
    assert.isFalse(association.readOnly)
  })

  test('creates an instance with cloned bindings', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const association = new DomainAssociation(dataDomain, parentKey, { bindings })
    assert.deepEqual(association.bindings, bindings)
    assert.notStrictEqual(association.bindings, bindings)
  })

  test('creates an instance with cloned semantics', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const association = new DomainAssociation(dataDomain, parentKey, { semantics })
    assert.deepEqual(association.semantics, semantics)
    assert.notStrictEqual(association.semantics, semantics)
  })

  test('creates an instance with cloned targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const association = new DomainAssociation(dataDomain, parentKey, { targets })
    assert.deepEqual(association.targets, targets)
    assert.notStrictEqual(association.targets, targets)
  })

  test('creates an instance with cloned schema', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, parentKey, { schema: schemaDefinition })
    assert.deepEqual(association.schema, schemaDefinition)
    assert.notStrictEqual(association.schema, schemaDefinition)
  })
})

test.group('DomainAssociation.toJSON()', () => {
  test('returns a valid JSON representation with default values', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const json = association.toJSON()
    assert.equal(json.kind, DomainAssociationKind)
    assert.typeOf(json.key, 'string')
    assert.deepEqual(json.info, Thing.fromJSON({ name: 'new_association' }).toJSON())
    assert.isUndefined(json.schema)
    assert.isUndefined(json.multiple)
    assert.isUndefined(json.required)
    assert.isUndefined(json.bindings)
    assert.isUndefined(json.targets)
    assert.isUndefined(json.semantics)
    assert.isUndefined(json.onDelete)
    assert.isUndefined(json.readOnly)
  })

  test('returns a JSON representation with provided key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const key = 'test-key'
    const association = new DomainAssociation(dataDomain, parentKey, { key })
    const json = association.toJSON()
    assert.equal(json.key, key)
  })

  test('returns a JSON representation with provided semantics', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const association = new DomainAssociation(dataDomain, parentKey, { semantics })
    const json = association.toJSON()
    assert.deepEqual(json.semantics, semantics, 'Semantics are serialized')
  })

  test('returns a JSON representation with provided onDelete rule', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const onDelete: OnDeleteRule = 'cascade'
    const association = new DomainAssociation(dataDomain, parentKey, { onDelete })
    const json = association.toJSON()
    assert.equal(json.onDelete, onDelete, 'onDelete is serialized')
  })

  test('returns a JSON representation with provided readOnly rule', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const readOnly = true
    const association = new DomainAssociation(dataDomain, parentKey, { readOnly })
    const json = association.toJSON()
    assert.isTrue(json.readOnly, 'readOnly is serialized when true')
  })

  test('returns a JSON representation with provided info', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const info = { name: 'Test Association', description: 'Test description' }
    const association = new DomainAssociation(dataDomain, parentKey, { info })
    const json = association.toJSON()
    assert.deepEqual(json.info, Thing.fromJSON(info).toJSON())
  })

  test('returns a JSON representation with provided schema', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, parentKey, { schema: schemaDefinition })
    const json = association.toJSON()
    assert.deepEqual(json.schema, schemaDefinition)
    assert.notStrictEqual(json.schema, schemaDefinition)
  })

  test('returns a JSON representation with provided multiple', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey, { multiple: true })
    const json = association.toJSON()
    assert.isTrue(json.multiple)
  })

  test('returns a JSON representation with provided required', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey, { required: true })
    const json = association.toJSON()
    assert.isTrue(json.required)
  })

  test('returns a JSON representation with provided bindings', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const association = new DomainAssociation(dataDomain, parentKey, { bindings })
    const json = association.toJSON()
    assert.deepEqual(json.bindings, bindings)
    assert.notStrictEqual(json.bindings, bindings)
  })

  test('returns a JSON representation with provided targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const association = new DomainAssociation(dataDomain, parentKey, { targets })
    const json = association.toJSON()
    assert.deepEqual(json.targets, targets)
    assert.notStrictEqual(json.targets, targets)
  })

  test('returns a JSON representation with all properties', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const key = 'test-key'
    const info = { name: 'Test Association', description: 'Test description' }
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const multiple = true
    const required = true
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: false } }]
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    const readOnly = true
    const onDelete: OnDeleteRule = 'restrict'

    const association = new DomainAssociation(dataDomain, parentKey, {
      key,
      info,
      schema: schemaDefinition,
      multiple,
      required,
      bindings,
      targets,
      semantics,
      onDelete,
      readOnly,
    })

    const json = association.toJSON()

    assert.equal(json.key, key)
    assert.deepEqual(json.info, Thing.fromJSON(info).toJSON())
    assert.deepEqual(json.schema, schemaDefinition)
    assert.equal(json.multiple, multiple)
    assert.equal(json.required, required)
    assert.deepEqual(json.bindings, bindings)
    assert.deepEqual(json.targets, targets)
    assert.deepEqual(json.semantics, semantics)
    assert.equal(json.onDelete, onDelete)
    assert.isTrue(json.readOnly)
  })

  test('returns a JSON representation with cloned bindings', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    const association = new DomainAssociation(dataDomain, parentKey, { bindings })
    const json = association.toJSON()
    assert.deepEqual(json.bindings, bindings)
    assert.notStrictEqual(json.bindings, bindings)
  })

  test('returns a JSON representation with cloned semantics when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    association.semantics = semantics
    const json = association.toJSON()
    assert.deepEqual(json.semantics, semantics, 'Semantics are serialized after creation')
  })

  test('returns a JSON representation with onDelete rule when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const onDelete: OnDeleteRule = 'setNull'
    association.onDelete = onDelete
    const json = association.toJSON()
    assert.equal(json.onDelete, onDelete, 'onDelete is serialized after creation')
  })

  test('returns a JSON representation with readOnly rule when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const readOnly = true
    association.readOnly = readOnly
    const json = association.toJSON()
    assert.isTrue(json.readOnly, 'readOnly is serialized after creation when true')
  })

  test('returns a JSON representation with cloned targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    const association = new DomainAssociation(dataDomain, parentKey, { targets })
    const json = association.toJSON()
    assert.deepEqual(json.targets, targets)
    assert.notStrictEqual(json.targets, targets)
  })

  test('returns a JSON representation with cloned schema', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, parentKey, { schema: schemaDefinition })
    const json = association.toJSON()
    assert.deepEqual(json.schema, schemaDefinition)
    assert.notStrictEqual(json.schema, schemaDefinition)
  })

  test('returns a JSON representation with cloned schema when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    association.schema = schemaDefinition
    const json = association.toJSON()
    assert.deepEqual(json.schema, schemaDefinition)
    assert.notStrictEqual(json.schema, schemaDefinition)
  })

  test('returns a JSON representation with cloned bindings when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const bindings: AssociationBinding[] = [{ type: 'web', schema: { hidden: true } }]
    association.bindings = bindings
    const json = association.toJSON()
    assert.deepEqual(json.bindings, bindings)
    assert.notStrictEqual(json.bindings, bindings)
  })

  test('returns a JSON representation with cloned targets when it is set after creation', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentKey = 'test-parent'
    const association = new DomainAssociation(dataDomain, parentKey)
    const targets = [{ key: 'target1', domain: 'domain1' }, { key: 'target2' }]
    association.targets = targets
    const json = association.toJSON()
    assert.deepEqual(json.targets, targets)
    assert.notStrictEqual(json.targets, targets)
  })
})

test.group('DomainAssociation.getParentInstance()', () => {
  test('returns the parent entity when the association is a child', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    const parent = association.getParentInstance()
    assert.deepEqual(parent, entity)
  })

  test('returns undefined when the association has no parent', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'non-existent-entity')
    dataDomain.graph.setNode(association.key, association)
    const parent = association.getParentInstance()
    assert.isUndefined(parent)
  })

  test('returns the correct parent when there are multiple levels of nesting', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    const parent = association.getParentInstance()
    assert.deepEqual(parent, entity)
  })

  test('returns undefined when the parent entity is removed from the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    dataDomain.graph.removeNode(entity.key)
    const parent = association.getParentInstance()
    assert.isUndefined(parent)
  })
})

test.group('DomainAssociation.remove()', () => {
  test('removes the association from the parent entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    association.remove()
    assert.isFalse(dataDomain.graph.hasNode(association.key))
    assert.deepEqual(entity.fields, [])
  })

  test('throws if the parent entity does not have the association', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity1 = model.addEntity()
    const entity2 = model.addEntity()
    const association = entity1.addAssociation()
    assert.throws(() => {
      entity2.removeAssociation(association.key)
    }, `Unable to find a connection between this entity and the ${association.key} association`)
  })

  test('removes the association from the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    association.remove()
    assert.isFalse(dataDomain.graph.hasNode(association.key))
  })

  test('removes the association from the parent entity fields', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = entity.addAssociation()
    association.remove()
    assert.deepEqual(entity.fields, [])
  })

  test('removes the association from the graph when it has targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity1 = model.addEntity()
    const entity2 = model.addEntity()
    const association = entity1.addAssociation()
    association.addTarget(entity2)
    association.remove()
    assert.isFalse(dataDomain.graph.hasNode(association.key))
    assert.isFalse(dataDomain.graph.hasEdge(association.key, entity2.key))
  })
})

test.group('DomainAssociation.ensureSchema()', () => {
  test('creates the schema object if it does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    assert.isUndefined(association.schema)
    const schema = association.ensureSchema()
    assert.isDefined(association.schema)
    assert.deepEqual(schema, {})
    assert.deepEqual(schema, schema)
  })

  test('returns the existing schema object if it exists', ({ assert }) => {
    const dataDomain = new DataDomain()
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, 'test-parent', { schema: schemaDefinition })
    const originalSchema = association.schema
    const schema = association.ensureSchema()
    assert.deepEqual(schema, schemaDefinition)
    assert.strictEqual(schema, originalSchema)
  })

  test('does not modify the existing schema object', ({ assert }) => {
    const dataDomain = new DataDomain()
    const schemaDefinition: AmfShapes.IApiAssociationShape = { linked: false }
    const association = new DomainAssociation(dataDomain, 'test-parent', { schema: schemaDefinition })
    const originalSchema = association.schema
    association.ensureSchema()
    assert.deepEqual(association.schema, schemaDefinition)
    assert.strictEqual(association.schema, originalSchema)
  })
})

test.group('DomainAssociation.isValidAttribute()', () => {
  test('returns true for valid data association attributes', ({ assert }) => {
    const validAttributes: DataFormat.DomainAttributeAttribute[] = ['required', 'multiple']
    validAttributes.forEach((attribute) => {
      assert.isTrue(DomainAssociation.isValidAttribute(attribute), `Attribute "${attribute}" should be valid`)
    })
  })

  test('returns false for invalid data association attributes', ({ assert }) => {
    assert.isFalse(DomainAssociation.isValidAttribute('invalid-attribute'))
    assert.isFalse(DomainAssociation.isValidAttribute(123))
    assert.isFalse(DomainAssociation.isValidAttribute(true))
    assert.isFalse(DomainAssociation.isValidAttribute({}))
    assert.isFalse(DomainAssociation.isValidAttribute([]))
    assert.isFalse(DomainAssociation.isValidAttribute(null))
    assert.isFalse(DomainAssociation.isValidAttribute(undefined))
  })

  test('returns false for non-string values', ({ assert }) => {
    assert.isFalse(DomainAssociation.isValidAttribute(123))
    assert.isFalse(DomainAssociation.isValidAttribute(true))
    assert.isFalse(DomainAssociation.isValidAttribute({}))
    assert.isFalse(DomainAssociation.isValidAttribute([]))
    assert.isFalse(DomainAssociation.isValidAttribute(null))
    assert.isFalse(DomainAssociation.isValidAttribute(undefined))
  })

  test('returns false for empty string', ({ assert }) => {
    assert.isFalse(DomainAssociation.isValidAttribute(''))
  })
})

test.group('DomainAssociation.getWebBinding()', () => {
  test('creates web bindings if they do not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const webBindings = association.getWebBinding()
    assert.isDefined(webBindings)
    assert.deepEqual(webBindings, {})
    assert.lengthOf(association.bindings, 1)
    assert.equal(association.bindings[0].type, 'web')
  })

  test('returns existing web bindings if they exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent', {
      bindings: [{ type: 'web', schema: { hidden: true } }],
    })
    const webBindings = association.getWebBinding()
    assert.isDefined(webBindings)
    assert.deepEqual(webBindings, { hidden: true })
    assert.lengthOf(association.bindings, 1)
    assert.equal(association.bindings[0].type, 'web')
  })

  test('does not modify existing web bindings object', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent', {
      bindings: [{ type: 'web', schema: { hidden: true } }],
    })
    const originalBindings = association.bindings[0].schema
    association.getWebBinding()
    assert.deepEqual(association.bindings[0].schema, { hidden: true })
    assert.deepEqual(association.bindings[0].schema, originalBindings)
  })

  test('notifies change when creating new bindings', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const webBindings = association.getWebBinding()
    assert.isDefined(webBindings)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  })
})

test.group('DomainAssociation.readBinding()', () => {
  test('returns undefined if no binding exists for the given type', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const binding = association.readBinding('web')
    assert.isUndefined(binding)
  })

  test('returns the web binding schema if it exists', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent', {
      bindings: [{ type: 'web', schema: { hidden: true } }],
    })
    const webBindings = association.readBinding('web')
    assert.deepEqual(webBindings, { hidden: true })
  })

  test('returns undefined if the binding exists but has no schema', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent', {
      // @ts-expect-error Testing undefined schema
      bindings: [{ type: 'web', schema: undefined }],
    })
    const webBindings = association.readBinding('web')
    assert.isUndefined(webBindings)
  })
})

test.group('DomainAssociation.addSemantic()', () => {
  test('adds a new semantic to the association', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    assert.deepInclude(association.semantics, semantic)
  })

  test('updates an existing semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic1 = { id: SemanticType.ResourceOwnerIdentifier, config: { value: 'old' } }
    const semantic2 = { id: SemanticType.ResourceOwnerIdentifier, config: { value: 'new' } }
    association.addSemantic(semantic1)
    association.addSemantic(semantic2)
    assert.lengthOf(association.semantics, 1)
    assert.deepInclude(association.semantics, semantic2)
  })

  test('throws an error if the semantic is not an association semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.User } // User is an Entity semantic
    assert.throws(() => {
      association.addSemantic(semantic)
    }, `Invalid semantic type: ${SemanticType.User}. Expected an association semantic.`)
  })

  test('notifies change when adding a new semantic', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  })

  test('notifies change when updating an existing semantic', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic1 = { id: SemanticType.ResourceOwnerIdentifier, config: { value: 'old' } }
    const semantic2 = { id: SemanticType.ResourceOwnerIdentifier, config: { value: 'new' } }
    association.addSemantic(semantic1)
    // Clear the event queue before the second add
    await new Promise((resolve) => setTimeout(resolve, 0))
    association.addSemantic(semantic2)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  })
})

test.group('DomainAssociation.removeSemantic()', () => {
  test('removes an existing semantic from the association', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    assert.deepInclude(association.semantics, semantic)
    association.removeSemantic(SemanticType.ResourceOwnerIdentifier)
    assert.notDeepInclude(association.semantics, semantic)
    assert.lengthOf(association.semantics, 0)
  })

  test('does nothing if the semantic to remove does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    const initialSemantics = [...association.semantics]
    association.removeSemantic('non-existent-semantic-id' as SemanticType)
    assert.deepEqual(association.semantics, initialSemantics)
  })

  test('notifies change when a semantic is removed', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    // Clear the event queue before removal
    await new Promise((resolve) => setTimeout(resolve, 0))
    association.removeSemantic(SemanticType.ResourceOwnerIdentifier)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  })

  test('does not notify change if the semantic to remove does not exist', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    // Clear the event queue before removal
    await new Promise((resolve) => setTimeout(resolve, 0))
    association.removeSemantic('non-existent-semantic-id' as SemanticType)
    await assert.notDispatches(dataDomain, 'change', { timeout: 20 })
  })
})

test.group('DomainAssociation.hasSemantic()', () => {
  test('returns true if the association has the specified semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    const semantic = { id: SemanticType.ResourceOwnerIdentifier }
    association.addSemantic(semantic)
    assert.isTrue(association.hasSemantic(SemanticType.ResourceOwnerIdentifier))
  })

  test('returns false if the association does not have the specified semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    assert.isFalse(association.hasSemantic(SemanticType.ResourceOwnerIdentifier))
  })

  test('returns false for an empty semantics array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'test-parent')
    assert.isFalse(association.hasSemantic(SemanticType.ResourceOwnerIdentifier))
  })
})

test.group('DomainAssociation.duplicate()', () => {
  test('duplicates an association successfully', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'originalAssociation'
    association.required = true
    association.multiple = false

    const duplicate = association.duplicate()

    assert.isDefined(duplicate)
    assert.notEqual(duplicate.key, association.key)
    assert.equal(duplicate.info.name, 'originalAssociation_copy')
    assert.equal(duplicate.required, true)
    assert.equal(duplicate.multiple, false)
  })

  test('places duplicate right after original association in fields list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    // Add multiple fields to test ordering
    const property1 = entity.addProperty({ info: { name: 'first' } })
    const association1 = entity.addAssociation({ key: targetEntity.key })
    association1.info.name = 'originalAssoc'
    const property2 = entity.addProperty({ info: { name: 'second' } })

    const duplicate = association1.duplicate()

    // Check that duplicate is placed right after association1
    const fieldKeys = entity.fields.map((f) => f.key)
    const association1Index = fieldKeys.indexOf(association1.key)
    const duplicateIndex = fieldKeys.indexOf(duplicate.key)

    assert.equal(duplicateIndex, association1Index + 1)
    assert.deepEqual(fieldKeys, [property1.key, association1.key, duplicate.key, property2.key])
  })

  test('generates unique name when base name conflicts', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'test'
    // Add another association that conflicts with the first generated name
    const conflictAssoc = entity.addAssociation({ key: targetEntity.key })
    conflictAssoc.info.name = 'test_copy'

    const duplicate = association.duplicate()

    assert.equal(duplicate.info.name, 'test_copy_2')
  })

  test('uses fallback name when original association has no name', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    // The association gets the default name "new_association" from schema creation
    assert.equal(association.info.name, 'new_association')

    const duplicate = association.duplicate()

    assert.equal(duplicate.info.name, 'new_association_copy')
  })

  test('uses field fallback when association name is explicitly undefined', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'test'
    // Manually set name to undefined to test the fallback logic
    association.info.name = undefined

    const duplicate = association.duplicate()

    assert.equal(duplicate.info.name, 'field_copy')
  })

  test('does not copy key property', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    const originalKey = association.key

    const duplicate = association.duplicate()

    assert.notEqual(duplicate.key, originalKey)
  })

  test('does not copy targets initially but recreates them', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity1 = model.addEntity()
    const targetEntity2 = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity1.key })
    association.addTarget(targetEntity2.key)
    association.info.name = 'multiTarget'

    const duplicate = association.duplicate()

    // Should have the same targets but different association instance
    const originalTargets = [...association.listTargets()]
    const duplicateTargets = [...duplicate.listTargets()]

    assert.equal(originalTargets.length, duplicateTargets.length)
    assert.equal(originalTargets.length, 2)

    // Check that target keys match
    const originalKeys = originalTargets.map((t) => t.key).sort()
    const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
    assert.deepEqual(originalKeys, duplicateKeys)
  })

  test('copies all other association attributes', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'complexAssociation'
    association.info.description = 'A complex association'
    association.required = true
    association.multiple = true
    association.readOnly = true
    association.onDelete = 'cascade'
    association.semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
    association.schema = { linked: true, unionType: 'allOf' }
    association.bindings = [{ type: 'web', schema: { hidden: true } }]

    const duplicate = association.duplicate()

    assert.equal(duplicate.info.name, 'complexAssociation_copy')
    assert.equal(duplicate.info.description, 'A complex association')
    assert.equal(duplicate.required, true)
    assert.equal(duplicate.multiple, true)
    assert.equal(duplicate.readOnly, true)
    assert.equal(duplicate.onDelete, 'cascade')
    assert.deepEqual(duplicate.semantics, [{ id: SemanticType.ResourceOwnerIdentifier }])
    assert.deepEqual(duplicate.schema, { linked: true, unionType: 'allOf' })
    assert.deepEqual(duplicate.bindings, [{ type: 'web', schema: { hidden: true } }])
  })

  test('creates independent copy of schema and bindings', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'original'
    association.schema = { linked: false, unionType: 'anyOf' }
    association.bindings = [{ type: 'web', schema: { hidden: false } }]

    const duplicate = association.duplicate()

    // Modify original schema and bindings
    association.schema!.linked = true
    association.bindings[0].schema = { hidden: true }

    // Duplicate should be unaffected
    assert.equal(duplicate.schema!.linked, false)
    assert.deepEqual(duplicate.bindings[0].schema, { hidden: false })
  })

  test('copies targets correctly', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()

    const targetEntity1 = model.addEntity()
    const targetEntity2 = model.addEntity()

    const association = entity.addAssociation()
    association.addTarget(targetEntity1.key)
    association.addTarget(targetEntity2.key)
    association.info.name = 'multiTargetAssoc'

    // Check that original association has targets
    const originalTargets = [...association.listTargets()]
    assert.equal(originalTargets.length, 2)

    const duplicate = association.duplicate()

    // Check targets on duplicate
    const duplicateTargets = [...duplicate.listTargets()]
    const duplicateTargetObjects = duplicate.targets

    // Check targets array structure
    assert.equal(duplicateTargetObjects.length, 2)
    assert.equal(duplicateTargetObjects[0].key, targetEntity1.key)
    assert.equal(duplicateTargetObjects[1].key, targetEntity2.key)

    // Check resolved targets
    assert.equal(duplicateTargets.length, 2)
    const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
    const expectedKeys = [targetEntity1.key, targetEntity2.key].sort()
    assert.deepEqual(duplicateKeys, expectedKeys)
  })

  test('notifies domain of changes', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'test'

    let notificationCalled = false
    const originalNotify = dataDomain.notifyChange
    dataDomain.notifyChange = () => {
      notificationCalled = true
      originalNotify.call(dataDomain)
    }

    association.duplicate()

    assert.isTrue(notificationCalled)

    // Restore original method
    dataDomain.notifyChange = originalNotify
  })

  test('throws error when association has no parent entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const association = new DomainAssociation(dataDomain, 'non-existent-parent', {
      info: { name: 'orphan' },
    })

    assert.throws(() => {
      association.duplicate()
    }, 'Cannot duplicate association')
  })

  test('throws error when association is not in parent entity fields list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const association = new DomainAssociation(dataDomain, entity.key, {
      info: { name: 'detached' },
    })

    // Add association to graph but not to entity fields
    dataDomain.graph.setNode(association.key, association)

    assert.throws(() => {
      association.duplicate()
    }, 'does not exist on the parent entity fields list')
  })

  test('works with empty entity fields list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()
    const association = entity.addAssociation({ key: targetEntity.key })
    association.info.name = 'only'

    const duplicate = association.duplicate()

    assert.equal(entity.fields.length, 2)
    assert.equal(entity.fields[0].key, association.key)
    assert.equal(entity.fields[1].key, duplicate.key)
  })

  test('handles duplication with mixed field types', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()

    const property1 = entity.addProperty({ info: { name: 'prop1' } })
    const association1 = entity.addAssociation({ key: targetEntity.key })
    association1.info.name = 'assoc1'
    const property2 = entity.addProperty({ info: { name: 'prop2' } })

    const duplicate = association1.duplicate()

    // Should maintain correct order
    const fieldKeys = entity.fields.map((f) => f.key)
    assert.deepEqual(fieldKeys, [property1.key, association1.key, duplicate.key, property2.key])
  })

  test('preserves field types correctly', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity = model.addEntity()
    const association = entity.addAssociation({ key: targetEntity.key })

    const duplicate = association.duplicate()

    // Check that both original and duplicate have correct field type
    const originalField = entity.fields.find((f) => f.key === association.key)
    const duplicateField = entity.fields.find((f) => f.key === duplicate.key)

    assert.equal(originalField?.type, 'association')
    assert.equal(duplicateField?.type, 'association')
  })

  test('handles multiple target entities correctly', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    const targetEntity1 = model.addEntity()
    const targetEntity2 = model.addEntity()
    const targetEntity3 = model.addEntity()

    const association = entity.addAssociation()
    association.addTarget(targetEntity1.key)
    association.addTarget(targetEntity2.key)
    association.addTarget(targetEntity3.key)
    association.info.name = 'multiTarget'

    const duplicate = association.duplicate()

    const originalTargets = [...association.listTargets()]
    const duplicateTargets = [...duplicate.listTargets()]

    assert.equal(originalTargets.length, 3)
    assert.equal(duplicateTargets.length, 3)

    // Verify all targets are copied correctly
    const originalKeys = originalTargets.map((t) => t.key).sort()
    const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
    assert.deepEqual(originalKeys, duplicateKeys)
  })

  test('handles foreign domains correctly', ({ assert }) => {
    const fd = new DataDomain()
    const fd1 = fd.addModel()
    const fe1 = fd1.addEntity()
    fd.info.version = '1.0.0'

    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    dataDomain.registerForeignDomain(fd)

    const association = entity.addAssociation({ key: fe1.key, domain: fd.key })

    const duplicate = association.duplicate()

    const originalTargets = [...association.listTargets()]
    const duplicateTargets = [...duplicate.listTargets()]

    assert.equal(originalTargets.length, 1)
    assert.equal(duplicateTargets.length, 1)

    // Verify all targets are copied correctly
    const originalKeys = originalTargets.map((t) => t.key).sort()
    const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
    assert.deepEqual(originalKeys, duplicateKeys)
  })
})
