circle core objects work again

This commit is contained in:
2025-03-11 08:49:47 +01:00
parent ac583741a4
commit 3da895083b
19 changed files with 778 additions and 1312 deletions

View File

@@ -64,10 +64,12 @@ os.symlink('${abs_dir_of_script}/lib', '${os.home_dir()}/.vmodules/freeflowunive
println('Herolib installation completed successfully!')
// Add vtest alias
addtoscript('alias vtest=', 'alias vtest=\'v -stats -enable-globals -n -w -cg -gc none -cc tcc test\' ') or {
addtoscript('alias vtest=', 'alias vtest=\'v -stats -enable-globals -show-c-output -n -w -cg -gc none -cc tcc test\' ') or {
eprintln('Failed to add vtest alias: ${err}')
}
//alias vtest='v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test'
addtoscript('HOME/hero/bin', 'export PATH="\$PATH:\$HOME/hero/bin"') or {
eprintln('Failed to add path to hero, ${err}')
}

View File

@@ -17,8 +17,8 @@ pub struct CircleCoordinator {
pub mut:
name string //is a unique name on planetary scale is a dns name
agents &core.AgentDB
// circles &core.CircleDB
// names &core.NameDB
circles &core.CircleDB
names &core.NameDB
session_state models.SessionState
}
@@ -54,13 +54,13 @@ pub fn new(args_ CircleCoordinatorArgs) !&CircleCoordinator {
// Initialize the db handlers with proper ourdb instances
mut agent_db := core.new_agentdb(session_state)!
// mut circle_db := core.new_circledb(session_state)!
// mut name_db := core.new_namedb(session_state)!
mut circle_db := core.new_circledb(session_state)!
mut name_db := core.new_namedb(session_state)!
mut cm := &CircleCoordinator{
agents: &agent_db
// circles: &circle_db
// names: &name_db
circles: &circle_db
names: &name_db
session_state: session_state
}

View File

@@ -0,0 +1,149 @@
module core
import freeflowuniverse.herolib.circles.models { DBHandler, SessionState }
import freeflowuniverse.herolib.circles.models.core { Circle }
@[heap]
pub struct CircleDB {
pub mut:
db DBHandler[Circle]
}
pub fn new_circledb(session_state SessionState) !CircleDB {
return CircleDB{
db: models.new_dbhandler[Circle]('circle', session_state)
}
}
pub fn (mut m CircleDB) new() Circle {
return Circle{}
}
// set adds or updates a circle
pub fn (mut m CircleDB) set(circle Circle) !Circle {
return m.db.set(circle)!
}
// get retrieves a circle by its ID
pub fn (mut m CircleDB) get(id u32) !Circle {
return m.db.get(id)!
}
// list returns all circle IDs
pub fn (mut m CircleDB) list() ![]u32 {
return m.db.list()!
}
pub fn (mut m CircleDB) getall() ![]Circle {
return m.db.getall()!
}
// delete removes a circle by its ID
pub fn (mut m CircleDB) delete(id u32) ! {
m.db.delete(id)!
}
//////////////////CUSTOM METHODS//////////////////////////////////
// get_by_name retrieves a circle by its name
pub fn (mut m CircleDB) get_by_name(name string) !Circle {
return m.db.get_by_key('name', name)!
}
// delete_by_name removes a circle by its name
pub fn (mut m CircleDB) delete_by_name(name string) ! {
// Get the circle by name
circle := m.get_by_name(name) or {
// Circle not found, nothing to delete
return
}
// Delete the circle by ID
m.delete(circle.id)!
}
// get_all_circle_names returns all circle names
pub fn (mut m CircleDB) get_all_circle_names() ![]string {
// Get all circle IDs
circle_ids := m.list()!
// Get names for all circles
mut names := []string{}
for id in circle_ids {
circle := m.get(id) or { continue }
names << circle.name
}
return names
}
// add_member adds a member to a circle
pub fn (mut m CircleDB) add_member(circle_name string, member core.Member) !Circle {
// Get the circle by name
mut circle := m.get_by_name(circle_name)!
// Check if member with same name already exists
for existing_member in circle.members {
if existing_member.name == member.name {
return error('Member with name ${member.name} already exists in circle ${circle_name}')
}
}
// Add the member
circle.members << member
// Save the updated circle
return m.set(circle)!
}
// remove_member removes a member from a circle by name
pub fn (mut m CircleDB) remove_member(circle_name string, member_name string) !Circle {
// Get the circle by name
mut circle := m.get_by_name(circle_name)!
// Find and remove the member
mut found := false
mut new_members := []core.Member{}
for member in circle.members {
if member.name == member_name {
found = true
continue
}
new_members << member
}
if !found {
return error('Member with name ${member_name} not found in circle ${circle_name}')
}
// Update the circle members
circle.members = new_members
// Save the updated circle
return m.set(circle)!
}
// update_member_role updates the role of a member in a circle
pub fn (mut m CircleDB) update_member_role(circle_name string, member_name string, new_role core.Role) !Circle {
// Get the circle by name
mut circle := m.get_by_name(circle_name)!
// Find and update the member
mut found := false
for i, mut member in circle.members {
if member.name == member_name {
circle.members[i].role = new_role
found = true
break
}
}
if !found {
return error('Member with name ${member_name} not found in circle ${circle_name}')
}
// Save the updated circle
return m.set(circle)!
}

View File

@@ -0,0 +1,192 @@
module core
import os
import rand
import freeflowuniverse.herolib.circles.actionprocessor
import freeflowuniverse.herolib.circles.models.core
fn test_circle_db() {
// Create a temporary directory for testing
test_dir := os.join_path(os.temp_dir(), 'hero_circle_test_${rand.intn(9000) or { 0 } + 1000}')
os.mkdir_all(test_dir) or { panic(err) }
defer { os.rmdir_all(test_dir) or {} }
mut runner := actionprocessor.new(path: test_dir)!
// Create multiple circles for testing
mut circle1 := runner.circles.new()
circle1.name = 'test-circle-1'
circle1.description = 'Test Circle 1'
mut circle2 := runner.circles.new()
circle2.name = 'test-circle-2'
circle2.description = 'Test Circle 2'
mut circle3 := runner.circles.new()
circle3.name = 'test-circle-3'
circle3.description = 'Test Circle 3'
// Create members for testing
mut member1 := core.Member{
name: 'member1'
description: 'Test Member 1'
role: .admin
pubkeys: ['pubkey1']
emails: ['member1@example.com']
}
mut member2 := core.Member{
name: 'member2'
description: 'Test Member 2'
role: .member
pubkeys: ['pubkey2']
emails: ['member2@example.com']
}
// Add members to circle1
circle1.members << member1
circle1.members << member2
// Add the circles
println('Adding circle 1')
circle1 = runner.circles.set(circle1)!
// Explicitly set different IDs for each circle to avoid overwriting
circle2.id = 1 // Set a different ID for circle2
println('Adding circle 2')
circle2 = runner.circles.set(circle2)!
circle3.id = 2 // Set a different ID for circle3
println('Adding circle 3')
circle3 = runner.circles.set(circle3)!
// Test list functionality
println('Testing list functionality')
// Get all circles
all_circles := runner.circles.getall()!
println('Retrieved ${all_circles.len} circles')
for i, circle in all_circles {
println('Circle ${i}: id=${circle.id}, name=${circle.name}')
}
assert all_circles.len == 3, 'Expected 3 circles, got ${all_circles.len}'
// Verify all circles are in the list
mut found1 := false
mut found2 := false
mut found3 := false
for circle in all_circles {
if circle.name == 'test-circle-1' {
found1 = true
} else if circle.name == 'test-circle-2' {
found2 = true
} else if circle.name == 'test-circle-3' {
found3 = true
}
}
assert found1, 'Circle 1 not found in list'
assert found2, 'Circle 2 not found in list'
assert found3, 'Circle 3 not found in list'
// Get and verify individual circles
println('Verifying individual circles')
retrieved_circle1 := runner.circles.get_by_name('test-circle-1')!
assert retrieved_circle1.name == circle1.name
assert retrieved_circle1.description == circle1.description
assert retrieved_circle1.members.len == 2
assert retrieved_circle1.members[0].name == 'member1'
assert retrieved_circle1.members[0].role == .admin
assert retrieved_circle1.members[1].name == 'member2'
assert retrieved_circle1.members[1].role == .member
// Test add_member method
println('Testing add_member method')
mut member3 := core.Member{
name: 'member3'
description: 'Test Member 3'
role: .contributor
pubkeys: ['pubkey3']
emails: ['member3@example.com']
}
runner.circles.add_member('test-circle-2', member3)!
updated_circle2 := runner.circles.get_by_name('test-circle-2')!
assert updated_circle2.members.len == 1
assert updated_circle2.members[0].name == 'member3'
assert updated_circle2.members[0].role == .contributor
// Test update_member_role method
println('Testing update_member_role method')
runner.circles.update_member_role('test-circle-2', 'member3', .stakeholder)!
role_updated_circle2 := runner.circles.get_by_name('test-circle-2')!
assert role_updated_circle2.members[0].role == .stakeholder
// Test remove_member method
println('Testing remove_member method')
runner.circles.remove_member('test-circle-1', 'member2')!
member_removed_circle1 := runner.circles.get_by_name('test-circle-1')!
assert member_removed_circle1.members.len == 1
assert member_removed_circle1.members[0].name == 'member1'
// Test get_all_circle_names method
println('Testing get_all_circle_names method')
circle_names := runner.circles.get_all_circle_names()!
assert circle_names.len == 3
assert 'test-circle-1' in circle_names
assert 'test-circle-2' in circle_names
assert 'test-circle-3' in circle_names
// Test delete functionality
println('Testing delete functionality')
// Delete circle 2
runner.circles.delete_by_name('test-circle-2')!
// Verify deletion with list
circles_after_delete := runner.circles.getall()!
assert circles_after_delete.len == 2, 'Expected 2 circles after deletion, got ${circles_after_delete.len}'
// Verify the remaining circles
mut found_after_delete1 := false
mut found_after_delete2 := false
mut found_after_delete3 := false
for circle in circles_after_delete {
if circle.name == 'test-circle-1' {
found_after_delete1 = true
} else if circle.name == 'test-circle-2' {
found_after_delete2 = true
} else if circle.name == 'test-circle-3' {
found_after_delete3 = true
}
}
assert found_after_delete1, 'Circle 1 not found after deletion'
assert !found_after_delete2, 'Circle 2 found after deletion (should be deleted)'
assert found_after_delete3, 'Circle 3 not found after deletion'
// Delete another circle
println('Deleting another circle')
runner.circles.delete_by_name('test-circle-3')!
// Verify only one circle remains
circles_after_second_delete := runner.circles.getall()!
assert circles_after_second_delete.len == 1, 'Expected 1 circle after second deletion, got ${circles_after_second_delete.len}'
assert circles_after_second_delete[0].name == 'test-circle-1', 'Remaining circle should be test-circle-1'
// Delete the last circle
println('Deleting last circle')
runner.circles.delete_by_name('test-circle-1')!
// Verify no circles remain
circles_after_all_deleted := runner.circles.getall() or {
// This is expected to fail with 'No circles found' error
assert err.msg().contains('No index keys defined for this type') || err.msg().contains('No circles found')
[]core.Circle{cap: 0}
}
assert circles_after_all_deleted.len == 0, 'Expected 0 circles after all deletions, got ${circles_after_all_deleted.len}'
println('All tests passed successfully')
}

View File

@@ -0,0 +1,194 @@
module core
import freeflowuniverse.herolib.circles.models { DBHandler, SessionState }
import freeflowuniverse.herolib.circles.models.core { Name, Record, RecordType }
@[heap]
pub struct NameDB {
pub mut:
db DBHandler[Name]
}
pub fn new_namedb(session_state SessionState) !NameDB {
return NameDB{
db: models.new_dbhandler[Name]('name', session_state)
}
}
pub fn (mut m NameDB) new() Name {
return Name{}
}
// set adds or updates a name
pub fn (mut m NameDB) set(name Name) !Name {
return m.db.set(name)!
}
// get retrieves a name by its ID
pub fn (mut m NameDB) get(id u32) !Name {
return m.db.get(id)!
}
// list returns all name IDs
pub fn (mut m NameDB) list() ![]u32 {
return m.db.list()!
}
pub fn (mut m NameDB) getall() ![]Name {
return m.db.getall()!
}
// delete removes a name by its ID
pub fn (mut m NameDB) delete(id u32) ! {
m.db.delete(id)!
}
//////////////////CUSTOM METHODS//////////////////////////////////
// get_by_domain retrieves a name by its domain
pub fn (mut m NameDB) get_by_domain(domain string) !Name {
return m.db.get_by_key('domain', domain)!
}
// delete_by_domain removes a name by its domain
pub fn (mut m NameDB) delete_by_domain(domain string) ! {
// Get the name by domain
name := m.get_by_domain(domain) or {
// Name not found, nothing to delete
return
}
// Delete the name by ID
m.delete(name.id)!
}
// get_all_domains returns all domains
pub fn (mut m NameDB) get_all_domains() ![]string {
// Get all name IDs
name_ids := m.list()!
// Get domains for all names
mut domains := []string{}
for id in name_ids {
name := m.get(id) or { continue }
domains << name.domain
}
return domains
}
// add_record adds a record to a name
pub fn (mut m NameDB) add_record(domain string, record Record) !Name {
// Get the name by domain
mut name := m.get_by_domain(domain)!
// Check if record with same name and type already exists
for existing_record in name.records {
if existing_record.name == record.name && existing_record.category == record.category {
return error('Record with name ${record.name} and type ${record.category} already exists in domain ${domain}')
}
}
// Add the record
name.records << record
// Save the updated name
return m.set(name)!
}
// remove_record removes a record from a name by record name and type
pub fn (mut m NameDB) remove_record(domain string, record_name string, record_type RecordType) !Name {
// Get the name by domain
mut name := m.get_by_domain(domain)!
// Find and remove the record
mut found := false
mut new_records := []Record{}
for record in name.records {
if record.name == record_name && record.category == record_type {
found = true
continue
}
new_records << record
}
if !found {
return error('Record with name ${record_name} and type ${record_type} not found in domain ${domain}')
}
// Update the name records
name.records = new_records
// Save the updated name
return m.set(name)!
}
// update_record_text updates the text of a record
pub fn (mut m NameDB) update_record_text(domain string, record_name string, record_type RecordType, new_text string) !Name {
// Get the name by domain
mut name := m.get_by_domain(domain)!
// Find and update the record
mut found := false
for i, mut record in name.records {
if record.name == record_name && record.category == record_type {
name.records[i].text = new_text
found = true
break
}
}
if !found {
return error('Record with name ${record_name} and type ${record_type} not found in domain ${domain}')
}
// Save the updated name
return m.set(name)!
}
// add_admin adds an admin to a name
pub fn (mut m NameDB) add_admin(domain string, pubkey string) !Name {
// Get the name by domain
mut name := m.get_by_domain(domain)!
// Check if admin already exists
if pubkey in name.admins {
return error('Admin with pubkey ${pubkey} already exists in domain ${domain}')
}
// Add the admin
name.admins << pubkey
// Save the updated name
return m.set(name)!
}
// remove_admin removes an admin from a name
pub fn (mut m NameDB) remove_admin(domain string, pubkey string) !Name {
// Get the name by domain
mut name := m.get_by_domain(domain)!
// Find and remove the admin
mut found := false
mut new_admins := []string{}
for admin in name.admins {
if admin == pubkey {
found = true
continue
}
new_admins << admin
}
if !found {
return error('Admin with pubkey ${pubkey} not found in domain ${domain}')
}
// Update the name admins
name.admins = new_admins
// Save the updated name
return m.set(name)!
}

View File

@@ -0,0 +1,209 @@
module core
import os
import rand
import freeflowuniverse.herolib.circles.actionprocessor
import freeflowuniverse.herolib.circles.models.core
fn test_name_db() {
// Create a temporary directory for testing
test_dir := os.join_path(os.temp_dir(), 'hero_name_test_${rand.intn(9000) or { 0 } + 1000}')
os.mkdir_all(test_dir) or { panic(err) }
defer { os.rmdir_all(test_dir) or {} }
mut runner := actionprocessor.new(path: test_dir)!
// Create multiple names for testing
mut name1 := runner.names.new()
name1.domain = 'example.com'
name1.description = 'Example Domain'
name1.admins = ['admin1_pubkey']
mut name2 := runner.names.new()
name2.domain = 'test.org'
name2.description = 'Test Organization'
name2.admins = ['admin2_pubkey']
mut name3 := runner.names.new()
name3.domain = 'herolib.io'
name3.description = 'HeroLib Website'
name3.admins = ['admin3_pubkey']
// Create records for testing
mut record1 := core.Record{
name: 'www'
text: 'Web server'
category: .a
addr: ['192.168.1.1', '192.168.1.2']
}
mut record2 := core.Record{
name: 'mail'
text: 'Mail server'
category: .mx
addr: ['192.168.2.1']
}
// Add records to name1
name1.records << record1
name1.records << record2
// Add the names
println('Adding name 1')
name1 = runner.names.set(name1)!
// Explicitly set different IDs for each name to avoid overwriting
name2.id = 1 // Set a different ID for name2
println('Adding name 2')
name2 = runner.names.set(name2)!
name3.id = 2 // Set a different ID for name3
println('Adding name 3')
name3 = runner.names.set(name3)!
// Test list functionality
println('Testing list functionality')
// Get all names
all_names := runner.names.getall()!
println('Retrieved ${all_names.len} names')
for i, name in all_names {
println('Name ${i}: id=${name.id}, domain=${name.domain}')
}
assert all_names.len == 3, 'Expected 3 names, got ${all_names.len}'
// Verify all names are in the list
mut found1 := false
mut found2 := false
mut found3 := false
for name in all_names {
if name.domain == 'example.com' {
found1 = true
} else if name.domain == 'test.org' {
found2 = true
} else if name.domain == 'herolib.io' {
found3 = true
}
}
assert found1, 'Name 1 not found in list'
assert found2, 'Name 2 not found in list'
assert found3, 'Name 3 not found in list'
// Get and verify individual names
println('Verifying individual names')
retrieved_name1 := runner.names.get_by_domain('example.com')!
assert retrieved_name1.domain == name1.domain
assert retrieved_name1.description == name1.description
assert retrieved_name1.records.len == 2
assert retrieved_name1.records[0].name == 'www'
assert retrieved_name1.records[0].category == .a
assert retrieved_name1.records[1].name == 'mail'
assert retrieved_name1.records[1].category == .mx
assert retrieved_name1.admins.len == 1
assert retrieved_name1.admins[0] == 'admin1_pubkey'
// Test add_record method
println('Testing add_record method')
mut record3 := core.Record{
name: 'api'
text: 'API server'
category: .a
addr: ['192.168.3.1']
}
runner.names.add_record('test.org', record3)!
updated_name2 := runner.names.get_by_domain('test.org')!
assert updated_name2.records.len == 1
assert updated_name2.records[0].name == 'api'
assert updated_name2.records[0].category == .a
assert updated_name2.records[0].text == 'API server'
// Test update_record_text method
println('Testing update_record_text method')
runner.names.update_record_text('test.org', 'api', .a, 'Updated API server')!
text_updated_name2 := runner.names.get_by_domain('test.org')!
assert text_updated_name2.records[0].text == 'Updated API server'
// Test remove_record method
println('Testing remove_record method')
runner.names.remove_record('example.com', 'mail', .mx)!
record_removed_name1 := runner.names.get_by_domain('example.com')!
assert record_removed_name1.records.len == 1
assert record_removed_name1.records[0].name == 'www'
// Test add_admin method
println('Testing add_admin method')
runner.names.add_admin('example.com', 'new_admin_pubkey')!
admin_added_name1 := runner.names.get_by_domain('example.com')!
assert admin_added_name1.admins.len == 2
assert 'new_admin_pubkey' in admin_added_name1.admins
// Test remove_admin method
println('Testing remove_admin method')
runner.names.remove_admin('example.com', 'admin1_pubkey')!
admin_removed_name1 := runner.names.get_by_domain('example.com')!
assert admin_removed_name1.admins.len == 1
assert admin_removed_name1.admins[0] == 'new_admin_pubkey'
// Test get_all_domains method
println('Testing get_all_domains method')
domains := runner.names.get_all_domains()!
assert domains.len == 3
assert 'example.com' in domains
assert 'test.org' in domains
assert 'herolib.io' in domains
// Test delete functionality
println('Testing delete functionality')
// Delete name 2
runner.names.delete_by_domain('test.org')!
// Verify deletion with list
names_after_delete := runner.names.getall()!
assert names_after_delete.len == 2, 'Expected 2 names after deletion, got ${names_after_delete.len}'
// Verify the remaining names
mut found_after_delete1 := false
mut found_after_delete2 := false
mut found_after_delete3 := false
for name in names_after_delete {
if name.domain == 'example.com' {
found_after_delete1 = true
} else if name.domain == 'test.org' {
found_after_delete2 = true
} else if name.domain == 'herolib.io' {
found_after_delete3 = true
}
}
assert found_after_delete1, 'Name 1 not found after deletion'
assert !found_after_delete2, 'Name 2 found after deletion (should be deleted)'
assert found_after_delete3, 'Name 3 not found after deletion'
// Delete another name
println('Deleting another name')
runner.names.delete_by_domain('herolib.io')!
// Verify only one name remains
names_after_second_delete := runner.names.getall()!
assert names_after_second_delete.len == 1, 'Expected 1 name after second deletion, got ${names_after_second_delete.len}'
assert names_after_second_delete[0].domain == 'example.com', 'Remaining name should be example.com'
// Delete the last name
println('Deleting last name')
runner.names.delete_by_domain('example.com')!
// Verify no names remain
names_after_all_deleted := runner.names.getall() or {
// This is expected to fail with 'No names found' error
assert err.msg().contains('No index keys defined for this type') || err.msg().contains('No names found')
[]core.Name{cap: 0}
}
assert names_after_all_deleted.len == 0, 'Expected 0 names after all deletions, got ${names_after_all_deleted.len}'
println('All tests passed successfully')
}

View File

@@ -74,7 +74,7 @@ pub fn (c Circle) dumps() ![]u8 {
}
// loads deserializes binary data into a Circle struct
pub fn Circle.loads(data []u8) !Circle {
pub fn circle_loads(data []u8) !Circle {
mut d := encoder.decoder_new(data)
mut circle := Circle{}

View File

@@ -1,6 +1,6 @@
module models
import freeflowuniverse.herolib.circles.models.core { agent_loads, Agent }
import freeflowuniverse.herolib.circles.models.core { agent_loads, Agent, circle_loads, Circle, name_loads, Name }
pub struct DBHandler[T] {
pub mut:
@@ -39,18 +39,24 @@ pub fn (mut m DBHandler[T]) get(id u32) !T {
item_data := m.session_state.dbs.db_data_core.get(id) or {
return error('Item data not found for ID ${id}')
}
mut o:= T{}
match o {
Agent {
o=agent_loads(item_data)!
}else{
return panic('Not implemented object')
}
}
// o.loads(item_data)!
o.id = id
return o
//THIS IS SUPER ANNOYING AND NOT NICE
$if T is Agent {
mut o:= agent_loads(item_data)!
o.id = id
return o
} $else $if T is Circle {
mut o:= circle_loads(item_data)!
o.id = id
return o
} $else $if T is Name {
mut o:= name_loads(item_data)!
o.id = id
return o
} $else {
return error('Unsupported type for deserialization')
}
panic("bug")
}
pub fn (mut m DBHandler[T]) exists(id u32) !bool {

View File

@@ -1,121 +0,0 @@
module core
import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.herolib.circles.models { DBHandler, SessionState }
import freeflowuniverse.herolib.circles.models.core { Agent, AgentService, AgentServiceAction, AgentState }
@[heap]
pub struct AgentDB {
pub mut:
db models.DBHandler[Agent]
}
pub fn new_agentdb(session_state SessionState) !AgentDB {
return AgentDB{
db:models.new_dbhandler[Agent]('agent', session_state)
}
}
pub fn (mut m AgentDB) new() Agent {
return Agent{}
}
// set adds or updates an agent
pub fn (mut m AgentDB) set(agent Agent) !Agent {
return m.db.set(agent)!
}
// get retrieves an agent by its ID
pub fn (mut m AgentDB) get(id u32) !Agent {
return m.db.get(id)!
}
// list returns all agent IDs
pub fn (mut m AgentDB) list() ![]u32 {
return m.db.list()!
}
pub fn (mut m AgentDB) getall() ![]Agent {
return m.db.getall()!
}
// delete removes an agent by its ID
pub fn (mut m AgentDB) delete(id u32) ! {
m.db.delete(id)!
}
//////////////////CUSTOM METHODS//////////////////////////////////
// get_by_pubkey retrieves an agent by its public key
pub fn (mut m AgentDB) get_by_pubkey(pubkey string) !Agent {
return m.db.get_by_key('pubkey', pubkey)!
}
// delete_by_pubkey removes an agent by its public key
pub fn (mut m AgentDB) delete_by_pubkey(pubkey string) ! {
// Get the agent by pubkey
agent := m.get_by_pubkey(pubkey) or {
// Agent not found, nothing to delete
return
}
// Delete the agent by ID
m.delete(agent.id)!
}
// update_status updates just the status of an agent
pub fn (mut m AgentDB) update_status(pubkey string, status AgentState) !Agent {
// Get the agent by pubkey
mut agent := m.get_by_pubkey(pubkey)!
// Update the status
agent.status.status = status
agent.status.timestamp_last = ourtime.now()
// Save the updated agent
return m.set(agent)!
}
// get_all_agent_pubkeys returns all agent pubkeys
pub fn (mut m AgentDB) get_all_agent_pubkeys() ![]string {
// Get all agent IDs
agent_ids := m.list()!
// Get pubkeys for all agents
mut pubkeys := []string{}
for id in agent_ids {
agent := m.get(id) or { continue }
pubkeys << agent.pubkey
}
return pubkeys
}
// get_by_service returns all agents that provide a specific service
pub fn (mut m AgentDB) get_by_service(actor string, action string) ![]Agent {
mut matching_agents := []Agent{}
// Get all agent IDs
agent_ids := m.list()!
// Filter agents that provide the specified service
for id in agent_ids {
// Get the agent by ID
agent := m.get(id) or { continue }
// Check if agent provides the specified service
for service in agent.services {
if service.actor == actor {
for service_action in service.actions {
if service_action.action == action {
matching_agents << agent
break
}
}
break
}
}
}
return matching_agents
}

View File

@@ -1,274 +0,0 @@
module core
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.data.radixtree
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.circles.models
@[heap]
pub struct CircleDB {
pub mut:
db models.DBHandler[Circle]
}
pub fn new_circledb(session_state models.SessionState) !CircleDB {
return CircleDB{
db: models.new_dbhandler[Circle]('circle', session_state)
}
}
pub fn (mut m CircleDB) new() Circle {
return Circle{}
}
// set adds or updates a circle
pub fn (mut m CircleDB) set(circle Circle) !Circle {
return m.db.set(circle)!
}
// get retrieves a circle by its ID
pub fn (mut m CircleDB) get(id u32) !Circle {
return m.db.get(id)!
}
// list returns all circle IDs
pub fn (mut m CircleDB) list() ![]u32 {
return m.db.list()!
}
pub fn (mut m CircleDB) getall() ![]Circle {
return m.db.getall()!
}
// delete removes a circle by its ID
pub fn (mut m CircleDB) delete(id u32) ! {
m.db.delete(id)!
}
//////////////////CUSTOM METHODS//////////////////////////////////
// get_by_name retrieves a circle by its name
pub fn (mut m CircleDB) get_by_name(name string) !Circle {
return m.db.get_by_key('name', name)!
}
// delete_by_name removes a circle by its name
pub fn (mut m CircleDB) delete_by_name(name string) ! {
// Get the circle by name
circle := m.get_by_name(name) or {
// Circle not found, nothing to delete
return
}
// Delete the circle by ID
m.delete(circle.id)!
}
// add_member adds a member to a circle
pub fn (mut m CircleDB) add_member(circle_id u32, member Member) !Circle {
// Get the circle by ID
mut circle := m.get(circle_id)!
// Check if member with the same pubkey already exists
if member.pubkeys.len == 0 {
return error('Member must have at least one pubkey')
}
for existing_member in circle.members {
for existing_pubkey in existing_member.pubkeys {
for pubkey in member.pubkeys {
if existing_pubkey == pubkey {
return error('Member with pubkey ${pubkey} already exists in circle ${circle.name}')
}
}
}
}
// Add the member to the circle
circle.members << member
// Save the updated circle
return m.set(circle)!
}
// remove_member removes a member from a circle by pubkey
pub fn (mut m CircleDB) remove_member(circle_id u32, pubkey string) !Circle {
// Get the circle by ID
mut circle := m.get(circle_id)!
// Find and remove the member with the specified pubkey
mut found := false
mut new_members := []Member{}
for member in circle.members {
mut has_pubkey := false
for p in member.pubkeys {
if p == pubkey {
has_pubkey = true
break
}
}
if !has_pubkey {
new_members << member
} else {
found = true
}
}
if !found {
return error('Member with pubkey ${pubkey} not found in circle ${circle.name}')
}
// Update the circle's members
circle.members = new_members
// Save the updated circle
return m.set(circle)!
}
// update_member_role updates the role of a member in a circle
pub fn (mut m CircleDB) update_member_role(circle_id u32, pubkey string, role Role) !Circle {
// Get the circle by ID
mut circle := m.get(circle_id)!
// Find and update the member with the specified pubkey
mut found := false
for i, mut member in circle.members {
for p in member.pubkeys {
if p == pubkey {
circle.members[i].role = role
found = true
break
}
}
if found {
break
}
}
if !found {
return error('Member with pubkey ${pubkey} not found in circle ${circle.name}')
}
// Save the updated circle
return m.set(circle)!
}
// get_members returns all members of a circle
pub fn (mut m CircleDB) get_members(circle_id u32) ![]Member {
// Get the circle by ID
circle := m.get(circle_id)!
return circle.members
}
// get_members_by_role returns all members of a circle with a specific role
pub fn (mut m CircleDB) get_members_by_role(circle_id u32, role Role) ![]Member {
// Get the circle by ID
circle := m.get(circle_id)!
// Filter members by role
mut members_with_role := []Member{}
for member in circle.members {
if member.role == role {
members_with_role << member
}
}
return members_with_role
}
// play processes heroscript commands for circles
pub fn play_circle(mut cm CircleDB, mut plbook playbook.PlayBook) ! {
// Find all actions that start with 'circle.'
circle_actions := plbook.actions_find(actor: 'circle')!
if circle_actions.len == 0 {
return
}
// Process circle.create actions
mut create_actions := plbook.actions_find(actor: 'circle', name: 'create')!
for mut action in create_actions {
mut p := action.params
// Create a new circle
mut circle := cm.new()
circle.name = p.get('name')!
circle.description = p.get_default('description', '')!
// Save the circle
circle = cm.set(circle)!
// Mark the action as done
action.done = true
// Return the created circle as a result
action.result.set('id', circle.id.str())
action.result.set('name', circle.name)
}
// Process circle.add_member actions
mut add_member_actions := plbook.actions_find(actor: 'circle', name: 'add_member')!
for mut action in add_member_actions {
mut p := action.params
// Get circle name
circle_name := p.get('circle')!
// Find the circle by name
mut circle := cm.get_by_name(circle_name) or {
action.result.set('error', 'Circle with name ${circle_name} not found')
action.done = true
continue
}
// Create a new member
mut member := Member{
name: p.get('name')!
description: p.get_default('description', '')!
}
// Get pubkeys if provided
if p.exists('pubkey') {
member.pubkeys << p.get('pubkey')!
} else if p.exists('pubkeys') {
member.pubkeys = p.get_list('pubkeys')!
}
// Get emails if provided
if p.exists('email') {
member.emails << p.get('email')!
} else if p.exists('emails') {
member.emails = p.get_list('emails')!
}
// Get role if provided
role_str := p.get_default('role', 'member')!
member.role = match role_str.to_lower() {
'admin' { Role.admin }
'stakeholder' { Role.stakeholder }
'member' { Role.member }
'contributor' { Role.contributor }
'guest' { Role.guest }
else { Role.member }
}
// Add the member to the circle
circle = cm.add_member(circle.id, member) or {
action.result.set('error', err.str())
action.done = true
continue
}
// Mark the action as done
action.done = true
// Return the member info as a result
action.result.set('circle_id', circle.id.str())
action.result.set('member_name', member.name)
action.result.set('role', role_str)
}
}

View File

@@ -1,206 +0,0 @@
module core
import os
import rand
fn test_circle_db() {
// Create a temporary directory for testing
test_dir := os.join_path(os.temp_dir(), 'hero_circle_test_${rand.intn(9000) or { 0 } + 1000}')
os.mkdir_all(test_dir) or { panic(err) }
defer { os.rmdir_all(test_dir) or {} }
mut runner := new(path: test_dir)!
// Create multiple circles for testing
mut circle1 := runner.circles.new()
circle1.name = 'Development Team'
circle1.description = 'Software development team'
mut circle2 := runner.circles.new()
circle2.name = 'Marketing Team'
circle2.description = 'Marketing and communications team'
mut circle3 := runner.circles.new()
circle3.name = 'Executive Team'
circle3.description = 'Executive leadership team'
// Add the circles
println('Adding circle 1')
circle1 = runner.circles.set(circle1)!
// Explicitly set different IDs for each circle to avoid overwriting
circle2.id = 1 // Set a different ID for circle2
println('Adding circle 2')
circle2 = runner.circles.set(circle2)!
circle3.id = 2 // Set a different ID for circle3
println('Adding circle 3')
circle3 = runner.circles.set(circle3)!
// Test list functionality
println('Testing list functionality')
// Debug: Print the circle IDs in the list
circle_ids := runner.circles.list()!
println('Circle IDs in list: ${circle_ids}')
// Get all circles
all_circles := runner.circles.getall()!
println('Retrieved ${all_circles.len} circles')
for i, circle in all_circles {
println('Circle ${i}: id=${circle.id}, name=${circle.name}')
}
assert all_circles.len == 3, 'Expected 3 circles, got ${all_circles.len}'
// Verify all circles are in the list
mut found1 := false
mut found2 := false
mut found3 := false
for circle in all_circles {
if circle.name == 'Development Team' {
found1 = true
} else if circle.name == 'Marketing Team' {
found2 = true
} else if circle.name == 'Executive Team' {
found3 = true
}
}
assert found1, 'Circle 1 not found in list'
assert found2, 'Circle 2 not found in list'
assert found3, 'Circle 3 not found in list'
// Get and verify individual circles
println('Verifying individual circles')
retrieved_circle1 := runner.circles.get_by_name('Development Team')!
assert retrieved_circle1.name == circle1.name
assert retrieved_circle1.description == circle1.description
assert retrieved_circle1.members.len == 0
// Test adding members to a circle
println('Testing adding members to a circle')
// Create members
mut member1 := Member{
pubkeys: ['dev-lead-pubkey']
name: 'Development Lead'
description: 'Lead developer'
role: .admin
emails: ['dev.lead@example.com']
}
mut member2 := Member{
pubkeys: ['dev-member-pubkey']
name: 'Developer'
description: 'Team developer'
role: .member
emails: ['developer@example.com']
}
mut member3 := Member{
pubkeys: ['contributor-pubkey']
name: 'Contributor'
description: 'External contributor'
role: .contributor
emails: ['contributor@example.com']
}
// Add members to circle 1
runner.circles.add_member(circle1.id, member1)!
runner.circles.add_member(circle1.id, member2)!
runner.circles.add_member(circle1.id, member3)!
// Verify members were added
updated_circle1 := runner.circles.get(circle1.id)!
assert updated_circle1.members.len == 3, 'Expected 3 members, got ${updated_circle1.members.len}'
// Test get_members functionality
println('Testing get_members functionality')
members := runner.circles.get_members(circle1.id)!
assert members.len == 3, 'Expected 3 members, got ${members.len}'
// Test get_members_by_role functionality
println('Testing get_members_by_role functionality')
admin_members := runner.circles.get_members_by_role(circle1.id, .admin)!
assert admin_members.len == 1, 'Expected 1 admin member, got ${admin_members.len}'
assert admin_members[0].pubkeys.len > 0
assert admin_members[0].pubkeys[0] == 'dev-lead-pubkey'
regular_members := runner.circles.get_members_by_role(circle1.id, .member)!
assert regular_members.len == 1, 'Expected 1 regular member, got ${regular_members.len}'
assert regular_members[0].pubkeys.len > 0
assert regular_members[0].pubkeys[0] == 'dev-member-pubkey'
contributor_members := runner.circles.get_members_by_role(circle1.id, .contributor)!
assert contributor_members.len == 1, 'Expected 1 contributor member, got ${contributor_members.len}'
assert contributor_members[0].pubkeys.len > 0
assert contributor_members[0].pubkeys[0] == 'contributor-pubkey'
// Test update_member_role functionality
println('Testing update_member_role functionality')
runner.circles.update_member_role(circle1.id, 'dev-member-pubkey', .stakeholder)!
// Verify role was updated
updated_circle_after_role_change := runner.circles.get(circle1.id)!
mut found_stakeholder := false
for member in updated_circle_after_role_change.members {
if member.pubkeys.len > 0 && member.pubkeys[0] == 'dev-member-pubkey' {
assert member.role == .stakeholder, 'Expected role to be stakeholder, got ${member.role}'
found_stakeholder = true
}
}
assert found_stakeholder, 'Stakeholder member not found after role update'
// Test remove_member functionality
println('Testing remove_member functionality')
runner.circles.remove_member(circle1.id, 'contributor-pubkey')!
// Verify member was removed
updated_circle_after_removal := runner.circles.get(circle1.id)!
assert updated_circle_after_removal.members.len == 2, 'Expected 2 members after removal, got ${updated_circle_after_removal.members.len}'
mut found_contributor := false
for member in updated_circle_after_removal.members {
if member.pubkeys.len > 0 && member.pubkeys[0] == 'contributor-pubkey' {
found_contributor = true
}
}
assert !found_contributor, 'Contributor still found after removal'
// Test delete_by_name functionality
println('Testing delete_by_name functionality')
runner.circles.delete_by_name('Marketing Team')!
// Verify deletion
circles_after_delete := runner.circles.getall()!
assert circles_after_delete.len == 2, 'Expected 2 circles after deletion, got ${circles_after_delete.len}'
mut found_marketing := false
for circle in circles_after_delete {
if circle.name == 'Marketing Team' {
found_marketing = true
}
}
assert !found_marketing, 'Marketing Team still found after deletion'
// Delete remaining circles
println('Deleting remaining circles')
runner.circles.delete_by_name('Development Team')!
runner.circles.delete_by_name('Executive Team')!
// Verify all circles are deleted
circles_after_all_deleted := runner.circles.getall() or {
// This is expected to fail with 'No circles found' error
assert err.msg().contains('not found'), 'Expected error message to contain "not found"'
[]Circle{}
}
assert circles_after_all_deleted.len == 0, 'Expected 0 circles after all deletions, got ${circles_after_all_deleted.len}'
println('All circle manager tests passed successfully')
}

View File

@@ -1,356 +0,0 @@
module core
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.data.radixtree
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.circles.models
@[heap]
pub struct NameDB {
pub mut:
db models.DBHandler[Name]
}
pub fn new_namedb(session_state models.SessionState) !NameDB {
return NameDB{
db: models.new_dbhandler[Name]('name', session_state)
}
}
pub fn (mut m NameDB) new() Name {
return Name{}
}
// set adds or updates a name
pub fn (mut m NameDB) set(name Name) !Name {
return m.db.set(name)!
}
// get retrieves a name by its ID
pub fn (mut m NameDB) get(id u32) !Name {
return m.db.get(id)!
}
// list returns all name IDs
pub fn (mut m NameDB) list() ![]u32 {
return m.db.list()!
}
pub fn (mut m NameDB) getall() ![]Name {
return m.db.getall()!
}
// delete removes a name by its ID
pub fn (mut m NameDB) delete(id u32) ! {
m.db.delete(id)!
}
//////////////////CUSTOM METHODS//////////////////////////////////
// get_by_domain retrieves a name by its domain
pub fn (mut m NameDB) get_by_domain(domain string) !Name {
return m.db.get_by_key('domain', domain)!
}
// delete_by_domain removes a name by its domain
pub fn (mut m NameDB) delete_by_domain(domain string) ! {
// Get the name by domain
name := m.get_by_domain(domain) or {
// Name not found, nothing to delete
return
}
// Delete the name by ID
m.delete(name.id)!
}
// add_record adds a record to a name
pub fn (mut m NameDB) add_record(name_id u32, record Record) !Name {
// Get the name by ID
mut name := m.get(name_id)!
// Check if record with the same name and category already exists
for existing_record in name.records {
if existing_record.name == record.name && existing_record.category == record.category {
return error('Record with name ${record.name} and category ${record.category} already exists in domain ${name.domain}')
}
}
// Add the record to the name
name.records << record
// Save the updated name
return m.set(name)!
}
// remove_record removes a record from a name by name and category
pub fn (mut m NameDB) remove_record(name_id u32, record_name string, category RecordType) !Name {
// Get the name by ID
mut name := m.get(name_id)!
// Find and remove the record with the specified name and category
mut found := false
mut new_records := []Record{}
for record in name.records {
if record.name != record_name || record.category != category {
new_records << record
} else {
found = true
}
}
if !found {
return error('Record with name ${record_name} and category ${category} not found in domain ${name.domain}')
}
// Update the name's records
name.records = new_records
// Save the updated name
return m.set(name)!
}
// update_record updates a record in a name
pub fn (mut m NameDB) update_record(name_id u32, record_name string, category RecordType, new_record Record) !Name {
// Get the name by ID
mut name := m.get(name_id)!
// Find and update the record with the specified name and category
mut found := false
for i, mut record in name.records {
if record.name == record_name && record.category == category {
// If the new record has a different name or category, check for conflicts
if (new_record.name != record_name || new_record.category != category) {
// Check for conflicts with existing records
for existing_record in name.records {
if existing_record.name == new_record.name && existing_record.category == new_record.category {
return error('Cannot update record: A record with name ${new_record.name} and category ${new_record.category} already exists')
}
}
}
name.records[i] = new_record
found = true
break
}
}
if !found {
return error('Record with name ${record_name} and category ${category} not found in domain ${name.domain}')
}
// Save the updated name
return m.set(name)!
}
// get_records returns all records of a name
pub fn (mut m NameDB) get_records(name_id u32) ![]Record {
// Get the name by ID
name := m.get(name_id)!
return name.records
}
// get_records_by_category returns all records of a name with a specific category
pub fn (mut m NameDB) get_records_by_category(name_id u32, category RecordType) ![]Record {
// Get the name by ID
name := m.get(name_id)!
// Filter records by category
mut records_with_category := []Record{}
for record in name.records {
if record.category == category {
records_with_category << record
}
}
return records_with_category
}
// add_admin adds an admin to a name
pub fn (mut m NameDB) add_admin(name_id u32, pubkey string) !Name {
// Get the name by ID
mut name := m.get(name_id)!
// Check if admin already exists
for admin in name.admins {
if admin == pubkey {
return error('Admin with pubkey ${pubkey} already exists in domain ${name.domain}')
}
}
// Add the admin to the name
name.admins << pubkey
// Save the updated name
return m.set(name)!
}
// remove_admin removes an admin from a name
pub fn (mut m NameDB) remove_admin(name_id u32, pubkey string) !Name {
// Get the name by ID
mut name := m.get(name_id)!
// Find and remove the admin with the specified pubkey
mut found := false
mut new_admins := []string{}
for admin in name.admins {
if admin != pubkey {
new_admins << admin
} else {
found = true
}
}
if !found {
return error('Admin with pubkey ${pubkey} not found in domain ${name.domain}')
}
// Ensure there's at least one admin left
if new_admins.len == 0 {
return error('Cannot remove the last admin from domain ${name.domain}')
}
// Update the name's admins
name.admins = new_admins
// Save the updated name
return m.set(name)!
}
// play processes heroscript commands for names
pub fn (mut m NameDB) play(mut plbook playbook.PlayBook) ! {
// Find all actions that start with 'name.'
name_actions := plbook.actions_find(actor: 'name')!
if name_actions.len == 0 {
return
}
// Process name.create actions
mut create_actions := plbook.actions_find(actor: 'name', name: 'create')!
for mut action in create_actions {
mut p := action.params
// Create a new name
mut name := m.new()
name.domain = p.get('domain')!
name.description = p.get_default('description', '')!
// Add admin if provided
if p.exists('admin') {
name.admins << p.get('admin')!
} else if p.exists('admins') {
name.admins = p.get_list('admins')!
}
// Save the name
name = m.set(name)!
// Mark the action as done
action.done = true
// Return the created name as a result
action.result.set('id', name.id.str())
action.result.set('domain', name.domain)
}
// Process name.add_record actions
mut add_record_actions := plbook.actions_find(actor: 'name', name: 'add_record')!
for mut action in add_record_actions {
mut p := action.params
// Get domain name
domain := p.get('domain')!
// Find the name by domain
mut name := m.get_by_domain(domain) or {
action.result.set('error', 'Domain ${domain} not found')
action.done = true
continue
}
// Create a new record
mut record := Record{
name: p.get('name')!
text: p.get_default('text', '')!
}
// Get record type
type_str := p.get('type')!
record.category = match type_str.to_lower() {
'a' { RecordType.a }
'aaaa' { RecordType.aaaa }
'cname' { RecordType.cname }
'mx' { RecordType.mx }
'ns' { RecordType.ns }
'ptr' { RecordType.ptr }
'soa' { RecordType.soa }
'srv' { RecordType.srv }
'txt' { RecordType.txt }
else {
action.result.set('error', 'Invalid record type: ${type_str}')
action.done = true
continue
}
}
// Get addresses
if p.exists('addr') {
record.addr << p.get('addr')!
} else if p.exists('addrs') {
record.addr = p.get_list('addrs')!
}
// Add the record to the name
name = m.add_record(name.id, record) or {
action.result.set('error', err.str())
action.done = true
continue
}
// Mark the action as done
action.done = true
// Return the record info as a result
action.result.set('domain_id', name.id.str())
action.result.set('record_name', record.name)
action.result.set('type', type_str)
}
// Process name.add_admin actions
mut add_admin_actions := plbook.actions_find(actor: 'name', name: 'add_admin')!
for mut action in add_admin_actions {
mut p := action.params
// Get domain name
domain := p.get('domain')!
// Find the name by domain
mut name := m.get_by_domain(domain) or {
action.result.set('error', 'Domain ${domain} not found')
action.done = true
continue
}
// Get admin pubkey
pubkey := p.get('pubkey')!
// Add the admin to the name
name = m.add_admin(name.id, pubkey) or {
action.result.set('error', err.str())
action.done = true
continue
}
// Mark the action as done
action.done = true
// Return the admin info as a result
action.result.set('domain_id', name.id.str())
action.result.set('pubkey', pubkey)
}
}

View File

@@ -1,328 +0,0 @@
module core
import os
import rand
fn test_name_db() {
// Create a temporary directory for testing
test_dir := os.join_path(os.temp_dir(), 'hero_name_test_${rand.intn(9000) or { 0 } + 1000}')
os.mkdir_all(test_dir) or { panic(err) }
defer { os.rmdir_all(test_dir) or {} }
mut runner := new(path: test_dir)!
// Create multiple names for testing
mut name1 := runner.names.new()
name1.domain = 'example.com'
name1.description = 'Example domain'
name1.admins = ['admin1-pubkey']
mut name2 := runner.names.new()
name2.domain = 'example.org'
name2.description = 'Example organization domain'
name2.admins = ['admin2-pubkey']
mut name3 := runner.names.new()
name3.domain = 'example.net'
name3.description = 'Example network domain'
name3.admins = ['admin3-pubkey']
// Add the names
println('Adding name 1')
name1 = runner.names.set(name1)!
// Explicitly set different IDs for each name to avoid overwriting
name2.id = 1 // Set a different ID for name2
println('Adding name 2')
name2 = runner.names.set(name2)!
name3.id = 2 // Set a different ID for name3
println('Adding name 3')
name3 = runner.names.set(name3)!
// Test list functionality
println('Testing list functionality')
// Debug: Print the name IDs in the list
name_ids := runner.names.list()!
println('Name IDs in list: ${name_ids}')
// Get all names
all_names := runner.names.getall()!
println('Retrieved ${all_names.len} names')
for i, name in all_names {
println('Name ${i}: id=${name.id}, domain=${name.domain}')
}
assert all_names.len == 3, 'Expected 3 names, got ${all_names.len}'
// Verify all names are in the list
mut found1 := false
mut found2 := false
mut found3 := false
for name in all_names {
if name.domain == 'example.com' {
found1 = true
} else if name.domain == 'example.org' {
found2 = true
} else if name.domain == 'example.net' {
found3 = true
}
}
assert found1, 'Name 1 not found in list'
assert found2, 'Name 2 not found in list'
assert found3, 'Name 3 not found in list'
// Get and verify individual names
println('Verifying individual names')
retrieved_name1 := runner.names.get_by_domain('example.com')!
assert retrieved_name1.domain == name1.domain
assert retrieved_name1.description == name1.description
assert retrieved_name1.records.len == 0
assert retrieved_name1.admins.len == 1
assert retrieved_name1.admins[0] == 'admin1-pubkey'
// Test adding records to a name
println('Testing adding records to a name')
// Create records
mut record1 := Record{
name: 'www'
text: 'Website'
category: .a
addr: ['192.168.1.1']
}
mut record2 := Record{
name: 'mail'
text: 'Mail server'
category: .mx
addr: ['mail.example.com']
}
mut record3 := Record{
name: 'txt'
text: 'SPF record'
category: .txt
addr: ['v=spf1 include:_spf.example.com ~all']
}
// Add records to name 1
runner.names.add_record(name1.id, record1)!
runner.names.add_record(name1.id, record2)!
runner.names.add_record(name1.id, record3)!
// Verify records were added
updated_name1 := runner.names.get(name1.id)!
assert updated_name1.records.len == 3, 'Expected 3 records, got ${updated_name1.records.len}'
// Test get_records functionality
println('Testing get_records functionality')
records := runner.names.get_records(name1.id)!
assert records.len == 3, 'Expected 3 records, got ${records.len}'
// Test get_records_by_category functionality
println('Testing get_records_by_category functionality')
a_records := runner.names.get_records_by_category(name1.id, .a)!
assert a_records.len == 1, 'Expected 1 A record, got ${a_records.len}'
assert a_records[0].name == 'www'
mx_records := runner.names.get_records_by_category(name1.id, .mx)!
assert mx_records.len == 1, 'Expected 1 MX record, got ${mx_records.len}'
assert mx_records[0].name == 'mail'
txt_records := runner.names.get_records_by_category(name1.id, .txt)!
assert txt_records.len == 1, 'Expected 1 TXT record, got ${txt_records.len}'
assert txt_records[0].name == 'txt'
// Test update_record functionality
println('Testing update_record functionality')
mut updated_record := Record{
name: 'www'
text: 'Updated website'
category: .a
addr: ['192.168.1.1', '192.168.1.2'] // Added a second IP
}
runner.names.update_record(name1.id, 'www', .a, updated_record)!
// Verify record was updated
updated_name_after_record_change := runner.names.get(name1.id)!
mut found_updated_record := false
for record in updated_name_after_record_change.records {
if record.name == 'www' && record.category == .a {
assert record.text == 'Updated website', 'Expected text to be "Updated website", got "${record.text}"'
assert record.addr.len == 2, 'Expected 2 addresses, got ${record.addr.len}'
found_updated_record = true
}
}
assert found_updated_record, 'Updated record not found after update'
// Test remove_record functionality
println('Testing remove_record functionality')
runner.names.remove_record(name1.id, 'txt', .txt)!
// Verify record was removed
updated_name_after_removal := runner.names.get(name1.id)!
assert updated_name_after_removal.records.len == 2, 'Expected 2 records after removal, got ${updated_name_after_removal.records.len}'
mut found_txt_record := false
for record in updated_name_after_removal.records {
if record.name == 'txt' && record.category == .txt {
found_txt_record = true
}
}
assert !found_txt_record, 'TXT record still found after removal'
// Test admin management
println('Testing admin management')
// Add admin
runner.names.add_admin(name1.id, 'admin2-pubkey')!
// Verify admin was added
updated_name_after_admin_add := runner.names.get(name1.id)!
assert updated_name_after_admin_add.admins.len == 2, 'Expected 2 admins after addition, got ${updated_name_after_admin_add.admins.len}'
assert 'admin2-pubkey' in updated_name_after_admin_add.admins, 'New admin not found after addition'
// Remove admin
runner.names.remove_admin(name1.id, 'admin2-pubkey')!
// Verify admin was removed
updated_name_after_admin_removal := runner.names.get(name1.id)!
assert updated_name_after_admin_removal.admins.len == 1, 'Expected 1 admin after removal, got ${updated_name_after_admin_removal.admins.len}'
assert 'admin2-pubkey' !in updated_name_after_admin_removal.admins, 'Removed admin still found after removal'
// Test delete_by_domain functionality
println('Testing delete_by_domain functionality')
runner.names.delete_by_domain('example.org')!
// Verify deletion
names_after_delete := runner.names.getall()!
assert names_after_delete.len == 2, 'Expected 2 names after deletion, got ${names_after_delete.len}'
mut found_org := false
for name in names_after_delete {
if name.domain == 'example.org' {
found_org = true
}
}
assert !found_org, 'example.org still found after deletion'
// Delete remaining names
println('Deleting remaining names')
runner.names.delete_by_domain('example.com')!
runner.names.delete_by_domain('example.net')!
// Verify all names are deleted
names_after_all_deleted := runner.names.getall() or {
// This is expected to fail with 'No names found' error
assert err.msg().contains('not found'), 'Expected error message to contain "not found"'
[]Name{}
}
assert names_after_all_deleted.len == 0, 'Expected 0 names after all deletions, got ${names_after_all_deleted.len}'
println('All name manager tests passed successfully')
}
fn test_name_play() {
test_dir := os.join_path(os.temp_dir(), 'hero_name_test_${rand.intn(9000) or { 0 } + 1000}')
os.mkdir_all(test_dir) or { panic(err) }
defer { os.rmdir_all(test_dir) or {} }
mut runner := new(path: test_dir)!
// Create heroscript for testing
heroscript_text := "
!!name.create
domain: 'example.org'
description: 'Example domain for testing'
admins: 'admin1-pubkey,admin2-pubkey'
!!name.add_record
domain: 'example.org'
name: 'www'
type: 'a'
addrs: '192.168.1.1,192.168.1.2'
text: 'Web server'
!!name.add_record
domain: 'example.org'
name: 'mail'
type: 'mx'
addr: '192.168.1.10'
text: 'Mail server'
!!name.add_admin
domain: 'example.org'
pubkey: 'admin3-pubkey'
"
// Parse the heroscript
mut pb := playbook.new(text: heroscript_text)!
name_manager:=runner.names
runner.names.play(mut pb)!
// Verify the domain was created
mut names := name_manager.getall()!
assert names.len == 1
// Get the created domain
mut name := name_manager.get_by_domain('example.org')!
// Verify domain properties
assert name.domain == 'example.org'
assert name.description == 'Example domain for testing'
// Verify admins
assert name.admins.len == 3
assert 'admin1-pubkey' in name.admins
assert 'admin2-pubkey' in name.admins
assert 'admin3-pubkey' in name.admins
// Verify records
assert name.records.len == 2
// Find and verify the www record
mut www_record := Record{}
mut mail_record := Record{}
for record in name.records {
if record.name == 'www' {
www_record = record
} else if record.name == 'mail' {
mail_record = record
}
}
// Verify www record
assert www_record.name == 'www'
assert www_record.category == RecordType.a
assert www_record.text == 'Web server'
assert www_record.addr.len == 2
assert www_record.addr[0] == '192.168.1.1'
assert www_record.addr[1] == '192.168.1.2'
// Verify mail record
assert mail_record.name == 'mail'
assert mail_record.category == RecordType.mx
assert mail_record.text == 'Mail server'
assert mail_record.addr.len == 1
assert mail_record.addr[0] == '192.168.1.10'
// No need to explicitly close the databases
println('Name play heroscript test passed successfully')
}

View File

@@ -19,9 +19,10 @@ pub fn decoder_new(data []u8) Decoder {
pub fn (mut d Decoder) get_string() !string {
n := d.get_u16()!
if n > 64 * 1024 { // 64KB limit
return error('string length ${n} exceeds 64KB limit')
}
//THIS IS ALWAYS TRYE BECAUSE u16 is max 64KB
// if n > 64 * 1024 { // 64KB limit
// return error('string length ${n} exceeds 64KB limit')
// }
if n > d.data.len {
return error('string length ${n} exceeds remaining data length ${d.data.len}')
}

View File

@@ -40,10 +40,8 @@ pub fn new(args OurDBConfig) !OurDB {
keysize = 2
} else if args.record_nr_max < 16777216 {
keysize = 3
} else if args.record_nr_max < 4294967296 {
keysize = 4
} else {
return error('max supported records is 4294967296 in OurDB')
keysize = 4
}
if f64(args.record_size_max * args.record_nr_max) / 2 > mbyte_ * 10 {