package aceboot::orm.tests
import aceboot::orm.common.*
import aceboot::orm.sqlite.*
import std.unittest.*
import std.unittest.testmacro.*
import aceboot::orm.macros.*
// F2 级联保存 cascade(to-one):保存父时自动持久化 owner 侧关系子对象并串好 FK。
// 独立前缀实体(f2_*)避免与既有测试实体重名(同包全局唯一)。
@Entity["f2_authors"]
public class _F2Author {
@Id[]
public var id: Int64 = 0
@Column[]
public var name: String = ""
}
@Entity["f2_books"]
public class _F2Book {
@Id[]
public var id: Int64 = 0
@Column[]
public var title: String = ""
// cascade=true:save/insert 本书时自动持久化未存的 author 并回填 FK。
@ManyToOne[_F2Author, "f2_authors", cascade = true]
public var author: ?_F2Author = None
}
// 无 cascade 的对照实体:关系不带 cascade,保存父不应持久化子。
@Entity["f2_books_nc"]
public class _F2BookNC {
@Id[]
public var id: Int64 = 0
@Column[]
public var title: String = ""
@ManyToOne[_F2Author, "f2_authors"]
public var author: ?_F2Author = None
}
func f2Setup(): DataSource {
let ds = DataSource(SqliteDriver(":memory:"))
Repository<_F2Author>(ds, _F2AuthorMapper()).createTable()
Repository<_F2Book>(ds, _F2BookMapper()).createTable()
Repository<_F2BookNC>(ds, _F2BookNCMapper()).createTable()
return ds
}
// save(book) 级联存未持久化的 author:author.id 被回填、book.authorId==author.id、findOne 取回 author。
@Test
func test_f2_cascade_save_to_one() {
let ds = f2Setup()
let bookRepo = Repository<_F2Book>(ds, _F2BookMapper())
let authorRepo = Repository<_F2Author>(ds, _F2AuthorMapper())
let a = _F2Author()
a.name = "Rowling"
let b = _F2Book()
b.title = "HP"
b.author = Some(a)
let bid = bookRepo.save(b)
@Expect(bid > 0, true)
// 子被级联持久化,id 经引用语义回填。
@Expect(a.id > 0, true)
@Expect(authorRepo.count(), 1)
// findOne 联表取回 author。
let back = bookRepo.findOne(DbInt(bid), ["author"])
@Expect(back.isSome(), true)
let loaded = back.getOrThrow()
@Expect(loaded.title, "HP")
@Expect(loaded.author.isSome(), true)
@Expect(loaded.author.getOrThrow().name, "Rowling")
@Expect(loaded.author.getOrThrow().id, a.id)
ds.driver.close()
}
// insert(book) 直调(非经 save)也触发级联。
@Test
func test_f2_cascade_insert_to_one() {
let ds = f2Setup()
let bookRepo = Repository<_F2Book>(ds, _F2BookMapper())
let authorRepo = Repository<_F2Author>(ds, _F2AuthorMapper())
let a = _F2Author()
a.name = "Tolkien"
let b = _F2Book()
b.title = "LOTR"
b.author = Some(a)
bookRepo.insert(b)
@Expect(a.id > 0, true)
@Expect(authorRepo.count(), 1)
// FK 落库正确:按 authorId 取回该书。
let byFk = bookRepo.findBy("authorId", DbInt(a.id))
@Expect(byFk.size, 1)
@Expect(byFk[0].title, "LOTR")
ds.driver.close()
}
// 默认 cascade=false:同结构但关系不带 cascade,save 父不持久化子(守卫精确)。
@Test
func test_f2_no_cascade_leaves_child_unsaved() {
let ds = f2Setup()
let ncRepo = Repository<_F2BookNC>(ds, _F2BookNCMapper())
let authorRepo = Repository<_F2Author>(ds, _F2AuthorMapper())
let a = _F2Author()
a.name = "Ghost"
let b = _F2BookNC()
b.title = "NC"
b.author = Some(a)
ncRepo.save(b)
// 子未被持久化:author.id 仍为 0,authors 表为空。
@Expect(a.id, 0)
@Expect(authorRepo.count(), 0)
ds.driver.close()
}
// 已持久化的子(id!=0)级联时走 update 分支,不重复插入。
@Test
func test_f2_cascade_existing_child_not_duplicated() {
let ds = f2Setup()
let bookRepo = Repository<_F2Book>(ds, _F2BookMapper())
let authorRepo = Repository<_F2Author>(ds, _F2AuthorMapper())
let a = _F2Author()
a.name = "Existing"
authorRepo.insert(a)
let aid = a.id
@Expect(aid > 0, true)
let b = _F2Book()
b.title = "B1"
b.author = Some(a)
bookRepo.save(b)
// 未新增 author 行(仍 1 条),FK 指向既有子。
@Expect(authorRepo.count(), 1)
@Expect(a.id, aid)
ds.driver.close()
}