From 3da895083b93d2c04bbc67f3aafccc173df8a7ab Mon Sep 17 00:00:00 2001 From: despiegk Date: Tue, 11 Mar 2025 08:49:47 +0100 Subject: [PATCH] circle core objects work again --- install_herolib.vsh | 4 +- lib/circles/actionprocessor/factory.v | 12 +- lib/circles/dbs/core/circle_db.v | 149 ++++++++ lib/circles/dbs/core/circle_db_test.v | 192 ++++++++++ lib/circles/dbs/core/name_db.v | 194 ++++++++++ lib/circles/dbs/core/name_db_test.v | 209 ++++++++++ .../models/{toooodo => core}/agent_test.v | 0 lib/circles/models/{toooodo => core}/circle.v | 2 +- .../models/{toooodo => core}/circle_test.v | 0 lib/circles/models/{toooodo => core}/name.v | 0 .../models/{toooodo => core}/name_test.v | 0 lib/circles/models/dbhandler.v | 32 +- lib/circles/models/toooodo/agent_db.v | 121 ------ lib/circles/models/toooodo/circle_db.v | 274 -------------- lib/circles/models/toooodo/circle_db_test.v | 206 ---------- lib/circles/models/toooodo/name_db.v | 356 ------------------ lib/circles/models/toooodo/name_db_test.v | 328 ---------------- lib/data/encoder/encoder_decode.v | 7 +- lib/data/ourdb/factory.v | 4 +- 19 files changed, 778 insertions(+), 1312 deletions(-) create mode 100644 lib/circles/dbs/core/circle_db.v create mode 100644 lib/circles/dbs/core/circle_db_test.v create mode 100644 lib/circles/dbs/core/name_db.v create mode 100644 lib/circles/dbs/core/name_db_test.v rename lib/circles/models/{toooodo => core}/agent_test.v (100%) rename lib/circles/models/{toooodo => core}/circle.v (98%) rename lib/circles/models/{toooodo => core}/circle_test.v (100%) rename lib/circles/models/{toooodo => core}/name.v (100%) rename lib/circles/models/{toooodo => core}/name_test.v (100%) delete mode 100644 lib/circles/models/toooodo/agent_db.v delete mode 100644 lib/circles/models/toooodo/circle_db.v delete mode 100644 lib/circles/models/toooodo/circle_db_test.v delete mode 100644 lib/circles/models/toooodo/name_db.v delete mode 100644 lib/circles/models/toooodo/name_db_test.v diff --git a/install_herolib.vsh b/install_herolib.vsh index 9097331c..f010ec47 100755 --- a/install_herolib.vsh +++ b/install_herolib.vsh @@ -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}') } diff --git a/lib/circles/actionprocessor/factory.v b/lib/circles/actionprocessor/factory.v index d7b76ad7..f1f780e3 100644 --- a/lib/circles/actionprocessor/factory.v +++ b/lib/circles/actionprocessor/factory.v @@ -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 } diff --git a/lib/circles/dbs/core/circle_db.v b/lib/circles/dbs/core/circle_db.v new file mode 100644 index 00000000..0b3b1d68 --- /dev/null +++ b/lib/circles/dbs/core/circle_db.v @@ -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)! +} diff --git a/lib/circles/dbs/core/circle_db_test.v b/lib/circles/dbs/core/circle_db_test.v new file mode 100644 index 00000000..b9582c57 --- /dev/null +++ b/lib/circles/dbs/core/circle_db_test.v @@ -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') +} diff --git a/lib/circles/dbs/core/name_db.v b/lib/circles/dbs/core/name_db.v new file mode 100644 index 00000000..bb9066a7 --- /dev/null +++ b/lib/circles/dbs/core/name_db.v @@ -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)! +} diff --git a/lib/circles/dbs/core/name_db_test.v b/lib/circles/dbs/core/name_db_test.v new file mode 100644 index 00000000..94be4d0d --- /dev/null +++ b/lib/circles/dbs/core/name_db_test.v @@ -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') +} diff --git a/lib/circles/models/toooodo/agent_test.v b/lib/circles/models/core/agent_test.v similarity index 100% rename from lib/circles/models/toooodo/agent_test.v rename to lib/circles/models/core/agent_test.v diff --git a/lib/circles/models/toooodo/circle.v b/lib/circles/models/core/circle.v similarity index 98% rename from lib/circles/models/toooodo/circle.v rename to lib/circles/models/core/circle.v index ed6bb3dd..db71e3ae 100644 --- a/lib/circles/models/toooodo/circle.v +++ b/lib/circles/models/core/circle.v @@ -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{} diff --git a/lib/circles/models/toooodo/circle_test.v b/lib/circles/models/core/circle_test.v similarity index 100% rename from lib/circles/models/toooodo/circle_test.v rename to lib/circles/models/core/circle_test.v diff --git a/lib/circles/models/toooodo/name.v b/lib/circles/models/core/name.v similarity index 100% rename from lib/circles/models/toooodo/name.v rename to lib/circles/models/core/name.v diff --git a/lib/circles/models/toooodo/name_test.v b/lib/circles/models/core/name_test.v similarity index 100% rename from lib/circles/models/toooodo/name_test.v rename to lib/circles/models/core/name_test.v diff --git a/lib/circles/models/dbhandler.v b/lib/circles/models/dbhandler.v index 23a05556..82645621 100644 --- a/lib/circles/models/dbhandler.v +++ b/lib/circles/models/dbhandler.v @@ -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 { diff --git a/lib/circles/models/toooodo/agent_db.v b/lib/circles/models/toooodo/agent_db.v deleted file mode 100644 index ee8fd52b..00000000 --- a/lib/circles/models/toooodo/agent_db.v +++ /dev/null @@ -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 -} diff --git a/lib/circles/models/toooodo/circle_db.v b/lib/circles/models/toooodo/circle_db.v deleted file mode 100644 index d8995072..00000000 --- a/lib/circles/models/toooodo/circle_db.v +++ /dev/null @@ -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) - } -} diff --git a/lib/circles/models/toooodo/circle_db_test.v b/lib/circles/models/toooodo/circle_db_test.v deleted file mode 100644 index fa0ae20f..00000000 --- a/lib/circles/models/toooodo/circle_db_test.v +++ /dev/null @@ -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') -} diff --git a/lib/circles/models/toooodo/name_db.v b/lib/circles/models/toooodo/name_db.v deleted file mode 100644 index b7734510..00000000 --- a/lib/circles/models/toooodo/name_db.v +++ /dev/null @@ -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) - } -} diff --git a/lib/circles/models/toooodo/name_db_test.v b/lib/circles/models/toooodo/name_db_test.v deleted file mode 100644 index 1517662c..00000000 --- a/lib/circles/models/toooodo/name_db_test.v +++ /dev/null @@ -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') -} diff --git a/lib/data/encoder/encoder_decode.v b/lib/data/encoder/encoder_decode.v index 675f0a5e..ac07ce2b 100644 --- a/lib/data/encoder/encoder_decode.v +++ b/lib/data/encoder/encoder_decode.v @@ -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}') } diff --git a/lib/data/ourdb/factory.v b/lib/data/ourdb/factory.v index 9f5b13b6..35b05a56 100644 --- a/lib/data/ourdb/factory.v +++ b/lib/data/ourdb/factory.v @@ -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 {