...
This commit is contained in:
93
examples/hero/heromodels/prd.vsh
Normal file
93
examples/hero/heromodels/prd.vsh
Normal file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.hero.heromodels
|
||||
|
||||
// Initialize database
|
||||
mut mydb := heromodels.new()!
|
||||
|
||||
// Create goals
|
||||
mut goals := [
|
||||
heromodels.Goal{
|
||||
id: 'G1'
|
||||
title: 'Faster Requirements'
|
||||
description: 'Reduce PRD creation time to under 1 day'
|
||||
gtype: .product
|
||||
}
|
||||
]
|
||||
|
||||
// Create use cases
|
||||
mut use_cases := [
|
||||
heromodels.UseCase{
|
||||
id: 'UC1'
|
||||
title: 'Generate PRD'
|
||||
actor: 'Product Manager'
|
||||
goal: 'Create validated PRD'
|
||||
steps: ['Select template', 'Fill fields', 'Export to Markdown']
|
||||
success: 'Complete PRD generated'
|
||||
failure: 'Validation failed'
|
||||
}
|
||||
]
|
||||
|
||||
// Create requirements
|
||||
mut criterion := heromodels.AcceptanceCriterion{
|
||||
id: 'AC1'
|
||||
description: 'Display template list'
|
||||
condition: 'List contains >= 5 templates'
|
||||
}
|
||||
|
||||
mut requirements := [
|
||||
heromodels.Requirement{
|
||||
id: 'R1'
|
||||
category: 'Editor'
|
||||
title: 'Template Selection'
|
||||
rtype: .functional
|
||||
description: 'User can select from templates'
|
||||
priority: .high
|
||||
criteria: [criterion]
|
||||
dependencies: []
|
||||
}
|
||||
]
|
||||
|
||||
// Create constraints
|
||||
mut constraints := [
|
||||
heromodels.Constraint{
|
||||
id: 'C1'
|
||||
title: 'ARM64 Support'
|
||||
description: 'Must run on ARM64 infrastructure'
|
||||
ctype: .technica
|
||||
}
|
||||
]
|
||||
|
||||
// Create risks
|
||||
mut risks := map[string]string{}
|
||||
risks['RISK1'] = 'Templates too limited → Add community contributions'
|
||||
risks['RISK2'] = 'AI suggestions inaccurate → Add review workflow'
|
||||
|
||||
// Create a new PRD object
|
||||
mut prd := mydb.prd.new(
|
||||
product_name: 'Lumina PRD Builder'
|
||||
version: 'v1.0'
|
||||
overview: 'Tool to create structured PRDs quickly'
|
||||
vision: 'Enable teams to generate clear requirements in minutes'
|
||||
goals: goals
|
||||
use_cases: use_cases
|
||||
requirements: requirements
|
||||
constraints: constraints
|
||||
risks: risks
|
||||
)!
|
||||
|
||||
// Save to database
|
||||
prd = mydb.prd.set(prd)!
|
||||
println('✓ Created PRD with ID: ${prd.id}')
|
||||
|
||||
// Retrieve from database
|
||||
mut retrieved := mydb.prd.get(prd.id)!
|
||||
println('✓ Retrieved PRD: ${retrieved.product_name}')
|
||||
|
||||
// List all PRDs
|
||||
mut all_prds := mydb.prd.list()!
|
||||
println('✓ Total PRDs in database: ${all_prds.len}')
|
||||
|
||||
// Check if exists
|
||||
exists := mydb.prd.exist(prd.id)!
|
||||
println('✓ PRD exists: ${exists}')
|
||||
@@ -31,6 +31,7 @@ pub mut:
|
||||
registration_desk DBRegistrationDesk
|
||||
messages DBMessages
|
||||
tags DBTags
|
||||
prd DBPrd
|
||||
rpc_handler &Handler
|
||||
}
|
||||
|
||||
@@ -91,6 +92,9 @@ pub fn new(args NewArgs) !&ModelsFactory {
|
||||
tags: DBTags{
|
||||
db: &mydb
|
||||
}
|
||||
prd: DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
rpc_handler: &h
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
module prd
|
||||
module heromodels
|
||||
|
||||
import incubaid.herolib.data.encoder
|
||||
import incubaid.herolib.data.ourtime
|
||||
import incubaid.herolib.hero.db
|
||||
import incubaid.herolib.schemas.jsonrpc { Response, new_error, new_response, new_response_false, new_response_int, new_response_true }
|
||||
import incubaid.herolib.hero.user { UserRef }
|
||||
import json
|
||||
|
||||
// Basic enums for clarity
|
||||
|
||||
// Core PRD type, this is the root object
|
||||
@[heap]
|
||||
pub struct ProductRequirementsDoc {
|
||||
pub:
|
||||
db.Base
|
||||
pub mut:
|
||||
product_name string
|
||||
version string
|
||||
overview string
|
||||
@@ -13,9 +22,12 @@ pub:
|
||||
use_cases []UseCase
|
||||
requirements []Requirement
|
||||
constraints []Constraint
|
||||
risks map[string]string // risk_id -> mitigation
|
||||
}
|
||||
|
||||
pub struct DBPrd {
|
||||
pub mut:
|
||||
db &db.DB @[skip; str: skip]
|
||||
}
|
||||
|
||||
pub enum PRDPriority {
|
||||
low
|
||||
@@ -87,7 +99,7 @@ pub enum ConstraintType {
|
||||
design
|
||||
}
|
||||
|
||||
pub struct constraint {
|
||||
pub struct Constraint {
|
||||
pub:
|
||||
id string
|
||||
title string
|
||||
@@ -95,3 +107,309 @@ pub:
|
||||
ctype ConstraintType
|
||||
}
|
||||
|
||||
pub fn (self ProductRequirementsDoc) type_name() string {
|
||||
return 'prd'
|
||||
}
|
||||
|
||||
pub fn (self ProductRequirementsDoc) description(methodname string) string {
|
||||
match methodname {
|
||||
'set' {
|
||||
return 'Create or update a product requirements document. Returns the ID of the PRD.'
|
||||
}
|
||||
'get' {
|
||||
return 'Retrieve a PRD by ID. Returns the complete PRD object.'
|
||||
}
|
||||
'delete' {
|
||||
return 'Delete a PRD by ID. Returns true if successful.'
|
||||
}
|
||||
'exist' {
|
||||
return 'Check if a PRD exists by ID. Returns true or false.'
|
||||
}
|
||||
'list' {
|
||||
return 'List all PRDs. Returns an array of PRD objects.'
|
||||
}
|
||||
else {
|
||||
return 'Generic method for PRD operations.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (self ProductRequirementsDoc) example(methodname string) (string, string) {
|
||||
match methodname {
|
||||
'set' {
|
||||
return '{"product_name": "Test Product", "version": "v1.0", "overview": "A test product", "vision": "To test the system", "goals": [], "use_cases": [], "requirements": [], "constraints": []}', '1'
|
||||
}
|
||||
'get' {
|
||||
return '{"id": 1}', '{"product_name": "Test Product", "version": "v1.0", "overview": "A test product", "vision": "To test the system", "goals": [], "use_cases": [], "requirements": [], "constraints": []}'
|
||||
}
|
||||
'delete' {
|
||||
return '{"id": 1}', 'true'
|
||||
}
|
||||
'exist' {
|
||||
return '{"id": 1}', 'true'
|
||||
}
|
||||
'list' {
|
||||
return '{}', '[{"product_name": "Test Product", "version": "v1.0"}]'
|
||||
}
|
||||
else {
|
||||
return '{}', '{}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (self ProductRequirementsDoc) dump(mut e encoder.Encoder) ! {
|
||||
e.add_string(self.product_name)
|
||||
e.add_string(self.version)
|
||||
e.add_string(self.overview)
|
||||
e.add_string(self.vision)
|
||||
|
||||
// Encode goals array
|
||||
e.add_u16(u16(self.goals.len))
|
||||
for goal in self.goals {
|
||||
e.add_string(goal.id)
|
||||
e.add_string(goal.title)
|
||||
e.add_string(goal.description)
|
||||
e.add_u8(u8(goal.gtype))
|
||||
}
|
||||
|
||||
// Encode use_cases array
|
||||
e.add_u16(u16(self.use_cases.len))
|
||||
for uc in self.use_cases {
|
||||
e.add_string(uc.id)
|
||||
e.add_string(uc.title)
|
||||
e.add_string(uc.actor)
|
||||
e.add_string(uc.goal)
|
||||
e.add_list_string(uc.steps)
|
||||
e.add_string(uc.success)
|
||||
e.add_string(uc.failure)
|
||||
}
|
||||
|
||||
// Encode requirements array
|
||||
e.add_u16(u16(self.requirements.len))
|
||||
for req in self.requirements {
|
||||
e.add_string(req.id)
|
||||
e.add_string(req.category)
|
||||
e.add_string(req.title)
|
||||
e.add_u8(u8(req.rtype))
|
||||
e.add_string(req.description)
|
||||
e.add_u8(u8(req.priority))
|
||||
|
||||
// Encode acceptance criteria
|
||||
e.add_u16(u16(req.criteria.len))
|
||||
for criterion in req.criteria {
|
||||
e.add_string(criterion.id)
|
||||
e.add_string(criterion.description)
|
||||
e.add_string(criterion.condition)
|
||||
}
|
||||
|
||||
// Encode dependencies
|
||||
e.add_list_string(req.dependencies)
|
||||
}
|
||||
|
||||
// Encode constraints array
|
||||
e.add_u16(u16(self.constraints.len))
|
||||
for constraint in self.constraints {
|
||||
e.add_string(constraint.id)
|
||||
e.add_string(constraint.title)
|
||||
e.add_string(constraint.description)
|
||||
e.add_u8(u8(constraint.ctype))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) load(mut o ProductRequirementsDoc, mut e encoder.Decoder) ! {
|
||||
o.product_name = e.get_string()!
|
||||
o.version = e.get_string()!
|
||||
o.overview = e.get_string()!
|
||||
o.vision = e.get_string()!
|
||||
|
||||
// Decode goals
|
||||
goals_len := e.get_u16()!
|
||||
mut goals := []Goal{}
|
||||
for _ in 0 .. goals_len {
|
||||
goals << Goal{
|
||||
id: e.get_string()!
|
||||
title: e.get_string()!
|
||||
description: e.get_string()!
|
||||
gtype: unsafe { GoalType(e.get_u8()!) }
|
||||
}
|
||||
}
|
||||
o.goals = goals
|
||||
|
||||
// Decode use_cases
|
||||
use_cases_len := e.get_u16()!
|
||||
mut use_cases := []UseCase{}
|
||||
for _ in 0 .. use_cases_len {
|
||||
use_cases << UseCase{
|
||||
id: e.get_string()!
|
||||
title: e.get_string()!
|
||||
actor: e.get_string()!
|
||||
goal: e.get_string()!
|
||||
steps: e.get_list_string()!
|
||||
success: e.get_string()!
|
||||
failure: e.get_string()!
|
||||
}
|
||||
}
|
||||
o.use_cases = use_cases
|
||||
|
||||
// Decode requirements
|
||||
requirements_len := e.get_u16()!
|
||||
mut requirements := []Requirement{}
|
||||
for _ in 0 .. requirements_len {
|
||||
req_id := e.get_string()!
|
||||
req_category := e.get_string()!
|
||||
req_title := e.get_string()!
|
||||
req_rtype := unsafe { RequirementType(e.get_u8()!) }
|
||||
req_description := e.get_string()!
|
||||
req_priority := unsafe { PRDPriority(e.get_u8()!) }
|
||||
|
||||
// Decode criteria
|
||||
criteria_len := e.get_u16()!
|
||||
mut criteria := []AcceptanceCriterion{}
|
||||
for _ in 0 .. criteria_len {
|
||||
criteria << AcceptanceCriterion{
|
||||
id: e.get_string()!
|
||||
description: e.get_string()!
|
||||
condition: e.get_string()!
|
||||
}
|
||||
}
|
||||
|
||||
// Decode dependencies
|
||||
dependencies := e.get_list_string()!
|
||||
|
||||
requirements << Requirement{
|
||||
id: req_id
|
||||
category: req_category
|
||||
title: req_title
|
||||
rtype: req_rtype
|
||||
description: req_description
|
||||
priority: req_priority
|
||||
criteria: criteria
|
||||
dependencies: dependencies
|
||||
}
|
||||
}
|
||||
o.requirements = requirements
|
||||
|
||||
// Decode constraints
|
||||
constraints_len := e.get_u16()!
|
||||
mut constraints := []Constraint{}
|
||||
for _ in 0 .. constraints_len {
|
||||
constraints << Constraint{
|
||||
id: e.get_string()!
|
||||
title: e.get_string()!
|
||||
description: e.get_string()!
|
||||
ctype: unsafe { ConstraintType(e.get_u8()!) }
|
||||
}
|
||||
}
|
||||
o.constraints = constraints
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PrdArg {
|
||||
pub mut:
|
||||
id u32
|
||||
product_name string @[required]
|
||||
version string
|
||||
overview string
|
||||
vision string
|
||||
goals []Goal
|
||||
use_cases []UseCase
|
||||
requirements []Requirement
|
||||
constraints []Constraint
|
||||
securitypolicy u32
|
||||
tags []string
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) new(args PrdArg) !ProductRequirementsDoc {
|
||||
mut o := ProductRequirementsDoc{
|
||||
product_name: args.product_name
|
||||
version: args.version
|
||||
overview: args.overview
|
||||
vision: args.vision
|
||||
goals: args.goals
|
||||
use_cases: args.use_cases
|
||||
requirements: args.requirements
|
||||
constraints: args.constraints
|
||||
updated_at: ourtime.now().unix()
|
||||
}
|
||||
|
||||
o.securitypolicy = args.securitypolicy
|
||||
o.tags = self.db.tags_get(args.tags)!
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) set(o ProductRequirementsDoc) !ProductRequirementsDoc {
|
||||
return self.db.set[ProductRequirementsDoc](o)!
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) delete(id u32) !bool {
|
||||
if !self.db.exists[ProductRequirementsDoc](id)! {
|
||||
return false
|
||||
}
|
||||
self.db.delete[ProductRequirementsDoc](id)!
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) exist(id u32) !bool {
|
||||
return self.db.exists[ProductRequirementsDoc](id)!
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) get(id u32) !ProductRequirementsDoc {
|
||||
mut o, data := self.db.get_data[ProductRequirementsDoc](id)!
|
||||
mut e_decoder := encoder.decoder_new(data)
|
||||
self.load(mut o, mut e_decoder)!
|
||||
return o
|
||||
}
|
||||
|
||||
pub fn (mut self DBPrd) list() ![]ProductRequirementsDoc {
|
||||
return self.db.list[ProductRequirementsDoc]()!.map(self.get(it)!)
|
||||
}
|
||||
|
||||
pub fn prd_handle(mut f ModelsFactory, rpcid int, servercontext map[string]string, userref UserRef, method string, params string) !Response {
|
||||
match method {
|
||||
'get' {
|
||||
id := db.decode_u32(params)!
|
||||
res := f.prd.get(id)!
|
||||
return new_response(rpcid, json.encode(res))
|
||||
}
|
||||
'set' {
|
||||
mut args := db.decode_generic[PrdArg](params)!
|
||||
mut o := f.prd.new(args)!
|
||||
if args.id != 0 {
|
||||
o.id = args.id
|
||||
}
|
||||
o = f.prd.set(o)!
|
||||
return new_response_int(rpcid, int(o.id))
|
||||
}
|
||||
'delete' {
|
||||
id := db.decode_u32(params)!
|
||||
deleted := f.prd.delete(id)!
|
||||
if deleted {
|
||||
return new_response_true(rpcid)
|
||||
} else {
|
||||
return new_error(rpcid,
|
||||
code: 404
|
||||
message: 'PRD with ID ${id} not found'
|
||||
)
|
||||
}
|
||||
}
|
||||
'exist' {
|
||||
id := db.decode_u32(params)!
|
||||
if f.prd.exist(id)! {
|
||||
return new_response_true(rpcid)
|
||||
} else {
|
||||
return new_response_false(rpcid)
|
||||
}
|
||||
}
|
||||
'list' {
|
||||
res := f.prd.list()!
|
||||
return new_response(rpcid, json.encode(res))
|
||||
}
|
||||
else {
|
||||
return new_error(rpcid,
|
||||
code: 32601
|
||||
message: 'Method ${method} not found on prd'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
226
lib/hero/heromodels/prd_test.v
Normal file
226
lib/hero/heromodels/prd_test.v
Normal file
@@ -0,0 +1,226 @@
|
||||
module heromodels
|
||||
|
||||
import incubaid.herolib.hero.db
|
||||
|
||||
fn test_prd_new() ! {
|
||||
mut mydb := db.new_test()!
|
||||
mut db_prd := DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
|
||||
mut args := PrdArg{
|
||||
product_name: 'Test Product'
|
||||
version: 'v1.0'
|
||||
overview: 'This is a test product.'
|
||||
vision: 'To revolutionize testing.'
|
||||
goals: []
|
||||
use_cases: []
|
||||
requirements: []
|
||||
constraints: []
|
||||
risks: {}
|
||||
}
|
||||
|
||||
prd := db_prd.new(args)!
|
||||
|
||||
assert prd.product_name == 'Test Product'
|
||||
assert prd.version == 'v1.0'
|
||||
assert prd.overview == 'This is a test product.'
|
||||
assert prd.vision == 'To revolutionize testing.'
|
||||
assert prd.goals.len == 0
|
||||
assert prd.use_cases.len == 0
|
||||
assert prd.requirements.len == 0
|
||||
assert prd.constraints.len == 0
|
||||
assert prd.risks.len == 0
|
||||
assert prd.updated_at > 0
|
||||
|
||||
println('✓ PRD new test passed!')
|
||||
}
|
||||
|
||||
fn test_prd_crud_operations() ! {
|
||||
mut mydb := db.new_test()!
|
||||
mut db_prd := DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
|
||||
// Create a new PRD
|
||||
mut args := PrdArg{
|
||||
product_name: 'CRUD Test Product'
|
||||
version: 'v1.0'
|
||||
overview: 'This is a test product for CRUD.'
|
||||
vision: 'To test CRUD operations.'
|
||||
goals: []
|
||||
use_cases: []
|
||||
requirements: []
|
||||
constraints: []
|
||||
risks: {}
|
||||
}
|
||||
|
||||
mut prd := db_prd.new(args)!
|
||||
prd = db_prd.set(prd)!
|
||||
original_id := prd.id
|
||||
|
||||
// Test get
|
||||
retrieved_prd := db_prd.get(original_id)!
|
||||
assert retrieved_prd.product_name == 'CRUD Test Product'
|
||||
assert retrieved_prd.version == 'v1.0'
|
||||
assert retrieved_prd.id == original_id
|
||||
|
||||
// Test exist
|
||||
exists := db_prd.exist(original_id)!
|
||||
assert exists == true
|
||||
|
||||
// Test delete
|
||||
db_prd.delete(original_id)!
|
||||
exists_after_delete := db_prd.exist(original_id)!
|
||||
assert exists_after_delete == false
|
||||
|
||||
println('✓ PRD CRUD operations test passed!')
|
||||
}
|
||||
|
||||
fn test_prd_encoding_decoding_complex() ! {
|
||||
mut mydb := db.new_test()!
|
||||
mut db_prd := DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
|
||||
mut goal := Goal{
|
||||
id: 'G1'
|
||||
title: 'Speed'
|
||||
description: 'Generate PRDs in minutes'
|
||||
gtype: .product
|
||||
}
|
||||
|
||||
mut use_case := UseCase{
|
||||
id: 'UC1'
|
||||
title: 'Create PRD'
|
||||
actor: 'Product Manager'
|
||||
goal: 'Produce PRD quickly'
|
||||
steps: ['Click new', 'Fill data', 'Export']
|
||||
success: 'Valid PRD generated'
|
||||
failure: 'Missing fields'
|
||||
}
|
||||
|
||||
mut criterion := AcceptanceCriterion{
|
||||
id: 'AC1'
|
||||
description: 'System displays template list'
|
||||
condition: 'List contains >= 5 templates'
|
||||
}
|
||||
|
||||
mut requirement := Requirement{
|
||||
id: 'R1'
|
||||
category: 'Editor'
|
||||
title: 'Template Selection'
|
||||
rtype: .functional
|
||||
description: 'User can select from predefined templates'
|
||||
priority: .high
|
||||
criteria: [criterion]
|
||||
dependencies: []
|
||||
}
|
||||
|
||||
mut constraint := Constraint{
|
||||
id: 'C1'
|
||||
title: 'ARM64 Only'
|
||||
description: 'Must run on ARM64 servers'
|
||||
ctype: .technica
|
||||
}
|
||||
|
||||
mut risks := map[string]string{}
|
||||
risks['RISK1'] = 'Mitigation strategy here'
|
||||
|
||||
mut args := PrdArg{
|
||||
product_name: 'Complex Test Product'
|
||||
version: 'v2.0'
|
||||
overview: 'Complete test with all fields'
|
||||
vision: 'Full feature test'
|
||||
goals: [goal]
|
||||
use_cases: [use_case]
|
||||
requirements: [requirement]
|
||||
constraints: [constraint]
|
||||
risks: risks
|
||||
}
|
||||
|
||||
mut prd := db_prd.new(args)!
|
||||
prd = db_prd.set(prd)!
|
||||
prd_id := prd.id
|
||||
|
||||
// Retrieve and verify
|
||||
retrieved_prd := db_prd.get(prd_id)!
|
||||
|
||||
assert retrieved_prd.product_name == 'Complex Test Product'
|
||||
assert retrieved_prd.goals.len == 1
|
||||
assert retrieved_prd.goals[0].id == 'G1'
|
||||
assert retrieved_prd.goals[0].gtype == .product
|
||||
|
||||
assert retrieved_prd.use_cases.len == 1
|
||||
assert retrieved_prd.use_cases[0].id == 'UC1'
|
||||
assert retrieved_prd.use_cases[0].steps.len == 3
|
||||
|
||||
assert retrieved_prd.requirements.len == 1
|
||||
assert retrieved_prd.requirements[0].id == 'R1'
|
||||
assert retrieved_prd.requirements[0].criteria.len == 1
|
||||
assert retrieved_prd.requirements[0].priority == .high
|
||||
|
||||
assert retrieved_prd.constraints.len == 1
|
||||
assert retrieved_prd.constraints[0].id == 'C1'
|
||||
assert retrieved_prd.constraints[0].ctype == .technica
|
||||
|
||||
assert retrieved_prd.risks.len == 1
|
||||
assert retrieved_prd.risks['RISK1'] == 'Mitigation strategy here'
|
||||
|
||||
println('✓ PRD encoding/decoding complex test passed!')
|
||||
}
|
||||
|
||||
fn test_prd_type_name() ! {
|
||||
mut mydb := db.new_test()!
|
||||
mut db_prd := DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
|
||||
mut args := PrdArg{
|
||||
product_name: 'Type Name Test'
|
||||
version: 'v1.0'
|
||||
overview: 'Test'
|
||||
vision: 'Test'
|
||||
goals: []
|
||||
use_cases: []
|
||||
requirements: []
|
||||
constraints: []
|
||||
risks: {}
|
||||
}
|
||||
|
||||
prd := db_prd.new(args)!
|
||||
type_name := prd.type_name()
|
||||
assert type_name == 'prd'
|
||||
|
||||
println('✓ PRD type_name test passed!')
|
||||
}
|
||||
|
||||
fn test_prd_list() ! {
|
||||
mut mydb := db.new_test()!
|
||||
mut db_prd := DBPrd{
|
||||
db: &mydb
|
||||
}
|
||||
|
||||
// Create multiple PRDs
|
||||
for i in 0 .. 3 {
|
||||
mut args := PrdArg{
|
||||
product_name: 'Product ${i}'
|
||||
version: 'v1.0'
|
||||
overview: 'Overview ${i}'
|
||||
vision: 'Vision ${i}'
|
||||
goals: []
|
||||
use_cases: []
|
||||
requirements: []
|
||||
constraints: []
|
||||
risks: {}
|
||||
}
|
||||
mut prd := db_prd.new(args)!
|
||||
prd = db_prd.set(prd)!
|
||||
}
|
||||
|
||||
// List all PRDs
|
||||
all_prds := db_prd.list()!
|
||||
assert all_prds.len == 3
|
||||
|
||||
println('✓ PRD list test passed!')
|
||||
}
|
||||
Reference in New Issue
Block a user