diff options
author | Bill MacAllister <whm@stanford.edu> | 2016-08-14 00:54:43 -0700 |
---|---|---|
committer | Bill MacAllister <whm@stanford.edu> | 2016-08-14 00:54:43 -0700 |
commit | 58f98fcb42c5b48c1686839f955a304bf2b844ca (patch) | |
tree | f003f769fec0dd825d62d989af1341e22770c80a /t | |
parent | fc4d04018c3a527bb9e43f2fdec959f85270fc74 (diff) |
Imported Upstream version 3.0.4
Diffstat (limited to 't')
27 files changed, 1500 insertions, 0 deletions
diff --git a/t/01-bdd-cucumber.t b/t/01-bdd-cucumber.t new file mode 100644 index 0000000..87babd3 --- /dev/null +++ b/t/01-bdd-cucumber.t @@ -0,0 +1,37 @@ +#!/usr/bin/perl + +BEGIN { + require './t/test-config.pl'; + if (!$RunDeveloperTests) { + print "1..0 # Skipped: Developer tests are not enabled"; + + exit; + } +}; + + +use strict; +use warnings; +use Devel::Cover; +use Test::More; + +# This will find step definitions and feature files in the directory you point +# it at below +use Test::BDD::Cucumber::Loader; + +# This harness prints out nice TAP +use Test::BDD::Cucumber::Harness::TestBuilder; + +# Load a directory with Cucumber files in it. It will recursively execute any +# file matching .*_steps.pl as a Step file, and .*\.feature as a feature file. +# The features are returned in @features, and the executor is created with the +# step definitions loaded. +my ( $executor, @features ) = Test::BDD::Cucumber::Loader->load( + 't/features/' ); + +# Create a Harness to execute against. TestBuilder harness prints TAP +my $harness = Test::BDD::Cucumber::Harness::TestBuilder->new({}); + +# For each feature found, execute it, using the Harness to print results +$executor->execute( $_, $harness ) for @features; +done_testing(); diff --git a/t/features/add.feature b/t/features/add.feature new file mode 100644 index 0000000..c7a0d61 --- /dev/null +++ b/t/features/add.feature @@ -0,0 +1,24 @@ +Feature: Adding entries to the directory
+ As a directory consumer
+ I want to ensure that I can add entries to the directory
+ In order to store information
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can add a new entry to the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ Then the new entry result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously add a new entry to the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And the test container has been deleted
diff --git a/t/features/bind.feature b/t/features/bind.feature new file mode 100644 index 0000000..74b19be --- /dev/null +++ b/t/features/bind.feature @@ -0,0 +1,40 @@ +Feature: Binding to the directory + As a directory consumer + I want to ensure that I can bind properly to directories + In order to establish my identity + + Background: + Given a usable Net::LDAPapi class + + Scenario: Can bind anonymously + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with anonymous authentication to the directory + Then the bind result is LDAP_SUCCESS + + Scenario: Can bind with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with simple authentication to the directory + Then the bind result is LDAP_SUCCESS + + Scenario: Can bind with sasl authentication + Given a Net::LDAPapi object that has been connected to the ldapi LDAP server + When I've bound with sasl authentication to the directory + Then the bind result is LDAP_SUCCESS + + Scenario: Can asynchronously bind anonymously + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with anonymous authentication to the directory + Then the bind result message type is LDAP_RES_BIND + And the bind result is LDAP_SUCCESS + + Scenario: Can asynchronously bind with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with simple authentication to the directory + Then the bind result message type is LDAP_RES_BIND + And the bind result is LDAP_SUCCESS + +# Scenario: Can asynchronously bind with sasl authentication +# Given a Net::LDAPapi object that has been connected to the ldapi LDAP server +# When I've asynchronously bound with sasl authentication to the directory +# Then the bind result message type is LDAP_RES_BIND +# And the bind result is LDAP_SUCCESS diff --git a/t/features/compare.feature b/t/features/compare.feature new file mode 100644 index 0000000..f6f8406 --- /dev/null +++ b/t/features/compare.feature @@ -0,0 +1,29 @@ +Feature: Comparing values to values of attributes of entries within the directory
+ As a directory consumer
+ I want to ensure that I can test the value of an attribute on an entry within the directory
+ In order to perform simple comparisons
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can compare an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've compared to an attribute on the new entry
+ Then the new entry result is LDAP_SUCCESS
+ And the new entry comparison result is LDAP_COMPARE_TRUE
+ And the test container has been deleted
+
+ Scenario: Can asynchronously compare an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously compared to an attribute on the new entry
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the new entry comparison result message type is LDAP_RES_COMPARE
+ And the new entry comparison result is LDAP_COMPARE_TRUE
+ And the test container has been deleted
diff --git a/t/features/delete.feature b/t/features/delete.feature new file mode 100644 index 0000000..663e08b --- /dev/null +++ b/t/features/delete.feature @@ -0,0 +1,29 @@ +Feature: Deleting entries from the directory
+ As a directory consumer
+ I want to ensure that I can delete entries from the directory
+ In order to remove information
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can remove an entry from the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've deleted the new entry from the directory
+ Then the new entry result is LDAP_SUCCESS
+ And the delete entry result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously remove an entry from the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously deleted the new entry from the directory
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the delete entry result message type is LDAP_RES_DELETE
+ And the delete entry result is LDAP_SUCCESS
+ And the test container has been deleted
diff --git a/t/features/extended_operations.feature b/t/features/extended_operations.feature new file mode 100644 index 0000000..ba922f5 --- /dev/null +++ b/t/features/extended_operations.feature @@ -0,0 +1,51 @@ +Feature: Executing extended operations against the directory + As a directory consumer + I want to ensure that I can execute extended operations against the directory + In order to use arbitrary LDAPv3 extensions + + Background: + Given a usable Net::LDAPapi class + + Scenario: Can match identities retrieved with native whoami and using extended operations with anonymous authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with anonymous authentication to the directory + And I've queried the directory for my identity + And I've issued a whoami extended operation to the directory + Then the identity result is LDAP_SUCCESS + And the whoami extended operation result is LDAP_SUCCESS + And the identity matches + And the whoami extended operation matches + + Scenario: Can match identities retrieved with native whoami and using extended operations with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with simple authentication to the directory + And I've queried the directory for my identity + And I've issued a whoami extended operation to the directory + Then the identity result is LDAP_SUCCESS + And the whoami extended operation result is LDAP_SUCCESS + And the identity matches + And the whoami extended operation matches + + Scenario: Can asynchronously match identities retrieved with native whoami and using extended operations with anonymous authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with anonymous authentication to the directory + And I've asynchronously queried the directory for my identity + And I've asynchronously issued a whoami extended operation to the directory + Then after waiting for all results, the identity result message type is LDAP_RES_EXTENDED + And the identity result is LDAP_SUCCESS + And after waiting for all results, the whoami extended operation result message type is LDAP_RES_EXTENDED + And the whoami extended operation result is LDAP_SUCCESS + And the identity matches + And the whoami extended operation matches + + Scenario: Can asynchronously match identities retrieved with native whoami and using extended operations with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with simple authentication to the directory + And I've asynchronously queried the directory for my identity + And I've asynchronously issued a whoami extended operation to the directory + Then after waiting for all results, the identity result message type is LDAP_RES_EXTENDED + And the identity result is LDAP_SUCCESS + And after waiting for all results, the whoami extended operation result message type is LDAP_RES_EXTENDED + And the whoami extended operation result is LDAP_SUCCESS + And the identity matches + And the whoami extended operation matches diff --git a/t/features/modify.feature b/t/features/modify.feature new file mode 100644 index 0000000..38fab2a --- /dev/null +++ b/t/features/modify.feature @@ -0,0 +1,83 @@ +Feature: Updating attributes of entries within the directory
+ As a directory consumer
+ I want to ensure that I can adjust attributes on entries within the directory
+ In order to extend or update entries with new or updated information
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can add a new attribute to an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've added a new attribute to the new entry
+ Then the new entry result is LDAP_SUCCESS
+ And the new attribute result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can modify an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've added a new attribute to the new entry
+ And I've modified the new attribute on the new entry
+ Then the new entry result is LDAP_SUCCESS
+ And the new attribute result is LDAP_SUCCESS
+ And the modified attribute result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can remove an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've added a new attribute to the new entry
+ And I've removed the new attribute from the new entry
+ Then the new entry result is LDAP_SUCCESS
+ And the new attribute result is LDAP_SUCCESS
+ And the removed attribute result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously add a new attribute to an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously added a new attribute to the new entry
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the new attribute result message type is LDAP_RES_MODIFY
+ And the new attribute result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously modify an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously added a new attribute to the new entry
+ And I've asynchronously modified the new attribute on the new entry
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the new attribute result message type is LDAP_RES_MODIFY
+ And the new attribute result is LDAP_SUCCESS
+ And after waiting for all results, the modified attribute result message type is LDAP_RES_MODIFY
+ And the modified attribute result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously remove an attribute on an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously added a new attribute to the new entry
+ And I've asynchronously removed the new attribute from the new entry
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the new attribute result message type is LDAP_RES_MODIFY
+ And the new attribute result is LDAP_SUCCESS
+ And after waiting for all results, the removed attribute result message type is LDAP_RES_MODIFY
+ And the removed attribute result is LDAP_SUCCESS
+ And the test container has been deleted
diff --git a/t/features/options.feature b/t/features/options.feature new file mode 100644 index 0000000..e432f04 --- /dev/null +++ b/t/features/options.feature @@ -0,0 +1,12 @@ +Feature: Control of options used to configure the LDAP library
+ As a directory consumer
+ I want to ensure that I can control the options that are used to configure the LDAP client library
+ In order to alter behaviour according to my needs
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can set and read back options
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've set option LDAP_OPT_SIZELIMIT with value 200
+ Then option LDAP_OPT_SIZELIMIT has value 200
diff --git a/t/features/rename.feature b/t/features/rename.feature new file mode 100644 index 0000000..653dcd0 --- /dev/null +++ b/t/features/rename.feature @@ -0,0 +1,34 @@ +Feature: Renaming entries within the directory
+ As a directory consumer
+ I want to ensure that I can rename entries within the directory
+ In order to reorganise information
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can rename an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've added a new entry to the directory
+ And I've added a new container to the directory
+ And I've moved the new entry to the new container
+ Then the new entry result is LDAP_SUCCESS
+ And the new container result is LDAP_SUCCESS
+ And the rename entry result is LDAP_SUCCESS
+ And the test container has been deleted
+
+ Scenario: Can asynchronously rename an entry within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've asynchronously bound with default authentication to the directory
+ And a test container has been created
+ And I've asynchronously added a new entry to the directory
+ And I've asynchronously added a new container to the directory
+ And I've asynchronously moved the new entry to the new container
+ Then after waiting for all results, the new entry result message type is LDAP_RES_ADD
+ And the new entry result is LDAP_SUCCESS
+ And after waiting for all results, the new container result message type is LDAP_RES_ADD
+ And the new container result is LDAP_SUCCESS
+ And after waiting for all results, the rename entry result message type is LDAP_RES_MODDN
+ And the rename entry result is LDAP_SUCCESS
+ And the test container has been deleted
diff --git a/t/features/search.feature b/t/features/search.feature new file mode 100644 index 0000000..88f43b8 --- /dev/null +++ b/t/features/search.feature @@ -0,0 +1,77 @@ +Feature: Searching the directory + As a directory consumer + I want to ensure that I can search the directory + In order to find relevant entries + + Background: + Given a usable Net::LDAPapi class + + Scenario: Can find objects that exist within the directory + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + + Scenario: Can asynchronously find objects that exist within the directory + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with default authentication to the directory + And I've asynchronously searched for records with scope LDAP_SCOPE_SUBTREE + Then after waiting for all results, the search result message type is LDAP_RES_SEARCH_RESULT + And the search result is LDAP_SUCCESS + And the search count matches + + Scenario: Can find objects that exist within the directory with a timeout + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE, with timeout 1 + Then the search result is LDAP_SUCCESS + And the search count matches + + Scenario: Can asynchronously find objects that exist within the directory with a timeout + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with default authentication to the directory + And I've asynchronously searched for records with scope LDAP_SCOPE_SUBTREE, with timeout 1 + Then after waiting for all results, the search result message type is LDAP_RES_SEARCH_RESULT + And the search result is LDAP_SUCCESS + And the search count matches + + Scenario: Can find objects that exist with the directory and iterate them with next_entry and next_attribute + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + And using next_entry for each entry returned the dn and all attributes using next_attribute are valid + + Scenario: Can find objects that exist with the directory and iterate them with next_entry and entry_attribute + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + And using next_entry for each entry returned the dn and all attributes using entry_attribute are valid + + Scenario: Can find objects that exist with the directory and iterate them with result_entry and next_attribute + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + And using result_entry for each entry returned the dn and all attributes using next_attribute are valid + + Scenario: Can find objects that exist with the directory and iterate them with result_entry and entry_attribute + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + And using result_entry for each entry returned the dn and all attributes using entry_attribute are valid + + Scenario: Can find objects that exist with the directory and read them all at once + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with default authentication to the directory + And I've searched for records with scope LDAP_SCOPE_SUBTREE + Then the search result is LDAP_SUCCESS + And the search count matches + And using get_all_entries for each entry returned the dn and all attributes are valid diff --git a/t/features/server_controls.feature b/t/features/server_controls.feature new file mode 100644 index 0000000..3b4cee4 --- /dev/null +++ b/t/features/server_controls.feature @@ -0,0 +1,21 @@ +Feature: Using server controls to control results
+ As a directory consumer
+ I want to ensure that I can use server controls when querying the directory
+ In order to be able to utilise the extended features of my directory
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can use the Server Side Sort control and Virtual List View Control
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ And the server side sort control definition
+ And the virtual list view control definition
+ When I've bound with default authentication to the directory
+ And I've created a server side sort control
+ And I've created a virtual list view control
+ And I've searched for records with scope LDAP_SCOPE_SUBTREE, with server controls server side sort and virtual list view
+ Then the search result is LDAP_SUCCESS
+ And the search count matches
+ And using next_entry for each entry returned the dn and all attributes using next_attribute are valid
+ And the server side sort control was successfully used
+ And the virtual list view control was successfully used
diff --git a/t/features/step_definitions/add_steps.pl b/t/features/step_definitions/add_steps.pl new file mode 100644 index 0000000..5615699 --- /dev/null +++ b/t/features/step_definitions/add_steps.pl @@ -0,0 +1,35 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've (asynchronously )?added a new (entry|container) to the directory/i, sub {
+ my $async = $1 ? 1 : 0;
+ my $type = lc($2);
+
+ S->{'new ' . $type . '_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'add_ext_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'add_ext';
+ }
+ S->{'new ' . $type . '_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{$type . '_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-mod'} = $TestConfig{'data'}{$type . '_attributes'};
+
+ S->{'new ' . $type . '_result'} = S->{'object'}->$func(%args);
+};
+
+1;
diff --git a/t/features/step_definitions/bind_steps.pl b/t/features/step_definitions/bind_steps.pl new file mode 100644 index 0000000..ed9fdaa --- /dev/null +++ b/t/features/step_definitions/bind_steps.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +#use Net::LDAPapi; +use Test::More; +use Test::BDD::Cucumber::StepFile; + +our %TestConfig = %main::TestConfig; + +When qr/I've (asynchronously )?bound with (.+?) authentication to the directory/, sub { + my $async = $1 ? 1 : 0; + my $type = lc($2); + + my $func = "bind_s"; + my %args = (); + + if ($async) { + $func = "bind"; + } + S->{'bind_async'} = $async; + + if ($type eq "default") { + $type = lc($TestConfig{'ldap'}{'default_bind_type'}); + } + S->{'bind_type'} = $type; + + S->{'bind_result'} = "skipped"; + + if ($type eq "anonymous") { + return if $TestConfig{'ldap'}{'bind_types'}{'anonymous'}{'enabled'} != 1; + } elsif ($type eq "simple") { + return if $TestConfig{'ldap'}{'bind_types'}{'simple'}{'enabled'} != 1; + + %args = ( + -dn => $TestConfig{'ldap'}{'bind_types'}{'simple'}{'bind_dn'}, + -password => $TestConfig{'ldap'}{'bind_types'}{'simple'}{'bind_pw'} + ); + } elsif ($type eq "sasl") { + return if $TestConfig{'ldap'}{'bind_types'}{'sasl'}{'enabled'} != 1; + + S->{'object'}->sasl_parms(%{$TestConfig{'ldap'}{'bind_types'}{'sasl'}{'sasl_parms'}}); + + %args = ( + -type => S->{'object'}->LDAP_AUTH_SASL + ); + } + + S->{'bind_result'} = S->{'object'}->$func(%args); +}; + +1; diff --git a/t/features/step_definitions/compare_steps.pl b/t/features/step_definitions/compare_steps.pl new file mode 100644 index 0000000..2b4a8c2 --- /dev/null +++ b/t/features/step_definitions/compare_steps.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've (asynchronously )?compared to an attribute on the new (entry|container)/i, sub {
+ my $async = $1 ? 1 : 0;
+ my $type = lc($2);
+
+ S->{'new ' . $type . ' comparison_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'compare_ext_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'compare_ext';
+ }
+ S->{'new ' . $type . ' comparison_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{$type . '_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-attr'} = $TestConfig{'compare'}{$type . '_attribute'};
+ $args{'-value'} = $TestConfig{'data'}{$type . '_attributes'}{$args{'-attr'}};
+
+ S->{'new ' . $type . ' comparison_result'} = S->{'object'}->$func(%args);
+};
+
+1;
diff --git a/t/features/step_definitions/delete_steps.pl b/t/features/step_definitions/delete_steps.pl new file mode 100644 index 0000000..41cddc6 --- /dev/null +++ b/t/features/step_definitions/delete_steps.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've (asynchronously )?deleted the new (entry|container) from the directory/i, sub {
+ my $async = $1 ? 1 : 0;
+ my $type = lc($2);
+
+ S->{'delete ' . $type . '_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'delete_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'delete';
+ }
+ S->{'delete ' . $type . '_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{$type . '_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+
+ S->{'delete ' . $type . '_result'} = S->{'object'}->$func(%args);
+};
+
+1;
diff --git a/t/features/step_definitions/extended_operation_steps.pl b/t/features/step_definitions/extended_operation_steps.pl new file mode 100644 index 0000000..d8c7c3a --- /dev/null +++ b/t/features/step_definitions/extended_operation_steps.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; +use Test::BDD::Cucumber::StepFile; + +our %TestConfig = %main::TestConfig; + + +When qr/I've (asynchronously )?issued a (.+?) extended operation to the directory/i, sub { + my $async = $1 ? 1 : 0; + my $extended_operation = lc($2); + + S->{$extended_operation . ' extended operation_result'} = "skipped"; + + return if S->{"bind_result"} eq "skipped"; + + my $func = "extended_operation_s"; + my %args = (); + + if ($async) { + $func = "extended_operation"; + } + S->{$extended_operation . ' extended operation_async'} = $async; + + if ($extended_operation eq "whoami") { + $args{'-oid'} = '1.3.6.1.4.1.4203.1.11.3'; + $args{'-result'} = \%{S->{'whoami extended operation_authzid'}}; + } + + S->{$extended_operation . ' extended operation_result'} = S->{'object'}->$func(%args); +}; + +Then qr/the (.+?) extended operation matches/i, sub { + my $extended_operation = lc($1); + + my $async = S->{$extended_operation . ' extended operation_async'}; + + my $got = undef; + if ($async) { + $got = {S->{'object'}->parse_extended_result(S->{$extended_operation . ' extended operation_result_id'})}; + } + + if ($extended_operation eq "whoami") { + if (!$async) { + $got = S->{$extended_operation . ' extended operation_authzid'}; + } + + is($got->{'retdatap'}, S->{'identity_got'}, 'Does ' . ($async ? 'asynchronous ' : '' ) . ' whoami extended_operation match native whoami?'); + } else { + TODO: { + todo_skip "$extended_operation matching unimplemented", 1; + } + } +}; + +1; diff --git a/t/features/step_definitions/general_steps.pl b/t/features/step_definitions/general_steps.pl new file mode 100644 index 0000000..40dbe5e --- /dev/null +++ b/t/features/step_definitions/general_steps.pl @@ -0,0 +1,104 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Net::LDAPapi; +use Test::More; +use Test::BDD::Cucumber::StepFile; + +our %TestConfig = %main::TestConfig; + +Given qr/a usable (\S+) class/, sub { use_ok($1); }; +Given qr/a Net::LDAPapi object that has been connected to the (.+?)?\s?LDAP server/, sub { + my $type = $1; + + if (!defined($type)) { + $type = $TestConfig{'ldap'}{'default_server'}; + } + + my $object = Net::LDAPapi->new(%{$TestConfig{'ldap'}{'server'}{$type}}); + + ok( $object, 'Net::LDAPapi object created'); + + S->{'object'} = $object; +}; + +When qr/a test container has been created/, sub { + my %args = (); + + $args{'-dn'} = sprintf('%s,%s', $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'}); + $args{'-mod'} = $TestConfig{'data'}{'test_container_attributes'}; + + my $status = S->{'object'}->add_s(%args); + + is(ldap_err2string($status), ldap_err2string(LDAP_SUCCESS), 'Was adding the test container successful?'); +}; + +Then qr/the test container has been deleted/, sub { + my %search_args = (); + my @delete_dns = (); + + $search_args{'-basedn'} = sprintf('%s,%s', $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'}); + $search_args{'-scope'} = LDAP_SCOPE_SUBTREE; + $search_args{'-filter'} = '(objectClass=*)'; + $search_args{'-attrs'} = ['objectClass']; + + my $search_status = S->{'object'}->search_s(%search_args); + + is(ldap_err2string($search_status), ldap_err2string(LDAP_SUCCESS), 'Was searching for the test container to delete successful?'); + + while( my $ent = S->{'object'}->result_entry) { + push(@delete_dns, S->{'object'}->get_dn()); + } + + foreach my $dn (sort { length($b) <=> length($a) } @delete_dns) { + my %delete_args = ('-dn' => $dn); + + my $status = S->{'object'}->delete_s(%delete_args); + is(ldap_err2string($status), ldap_err2string(LDAP_SUCCESS), 'Was deleting test container contents "' . $dn . '" successful?'); + } +}; + +Then qr/(after waiting for all results, )?the (.+) result message type is (.+)/, sub { + my $wait_for_all = $1 ? 1 : 0; + my $test_function = $2; + my $desired_result = $3; + + SKIP: { + + skip(C->{'scenario'}->{'name'} . " skipped", 1) if S->{$test_function . '_result'} eq "skipped"; + + isnt( S->{$test_function . '_result'}, undef, "Do we have result from $test_function?"); + + if (is( S->{$test_function . '_async'}, 1, "Was $test_function asynchronous?")) { + S->{$test_function . '_result_id'} = S->{'object'}->result(S->{$test_function . '_result'}, $wait_for_all, 1); + + is(S->{'object'}->msgtype2str(S->{'object'}->{"status"}), $desired_result, "Does expected result message type match?"); + } + + } +}; + +Then qr/the (.+) result is (.+)/, sub { + my $test_function = $1; + my $desired_result = $2; + + SKIP: { + + skip(C->{'scenario'}->{'name'} . " skipped", 1) if S->{$test_function . '_result'} eq "skipped"; + + if (isnt( S->{$test_function . '_result'}, undef, "Do we have result from $test_function?")) { + + if (S->{$test_function . '_async'}) { + my $ref = {S->{'object'}->parse_result(S->{$test_function . '_result_id'})}; + + is(ldap_err2string($ref->{'errcode'}), ldap_err2string(S->{'object'}->$desired_result), "Does expected async result code match?"); + } else { + is(ldap_err2string(S->{$test_function . '_result'}), ldap_err2string(S->{'object'}->$desired_result), "Does expected result code match?"); + } + } + } +}; + +1; diff --git a/t/features/step_definitions/modify_steps.pl b/t/features/step_definitions/modify_steps.pl new file mode 100644 index 0000000..6e6e92d --- /dev/null +++ b/t/features/step_definitions/modify_steps.pl @@ -0,0 +1,76 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've (asynchronously )?added a new attribute to the new entry/i, sub {
+ my $async = $1 ? 1 : 0;
+
+ S->{'new attribute_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'modify_ext_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'modify_ext';
+ }
+ S->{'new attribute_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{'entry_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-mod'} = $TestConfig{'modify'}{'new_attribute'};
+
+ S->{'new attribute_result'} = S->{'object'}->$func(%args);
+};
+
+When qr/I've (asynchronously )?modified the new attribute on the new entry/i, sub {
+ my $async = $1 ? 1 : 0;
+
+ S->{'modified attribute_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'modify_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'modify';
+ }
+ S->{'modified attribute_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{'entry_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-mod'} = $TestConfig{'modify'}{'modify_attribute'};
+
+ S->{'modified attribute_result'} = S->{'object'}->$func(%args);
+};
+
+When qr/I've (asynchronously )?removed the new attribute from the new entry/i, sub {
+ my $async = $1 ? 1 : 0;
+
+ S->{'removed attribute_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'modify_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'modify';
+ }
+ S->{'removed attribute_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{'entry_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-mod'} = $TestConfig{'modify'}{'remove_attribute'};
+
+ S->{'removed attribute_result'} = S->{'object'}->$func(%args);
+};
+
+1;
diff --git a/t/features/step_definitions/options_steps.pl b/t/features/step_definitions/options_steps.pl new file mode 100644 index 0000000..43e0b6a --- /dev/null +++ b/t/features/step_definitions/options_steps.pl @@ -0,0 +1,33 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Net::LDAPapi;
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+When qr/I've set option (.+?) with value (.+)/i, sub {
+ my $option = $1;
+ my $value = int($2);
+
+ my $status = S->{'object'}->set_option(S->{'object'}->$option, $value);
+
+ is(ldap_err2string($status), ldap_err2string(LDAP_SUCCESS), "Was option successfully set?");
+};
+
+Then qr/option (.+?) has value (.+)/i, sub {
+ my $option = $1;
+ my $value = $2;
+
+ my $data;
+
+ my $status = S->{'object'}->get_option(S->{'object'}->$option, \$data);
+
+ is(ldap_err2string($status), ldap_err2string(LDAP_SUCCESS), "Was option successfully retrieved?");
+ is($data, $value, "Is the option set to the expected value?");
+};
+
+1;
diff --git a/t/features/step_definitions/rename_steps.pl b/t/features/step_definitions/rename_steps.pl new file mode 100644 index 0000000..3005c52 --- /dev/null +++ b/t/features/step_definitions/rename_steps.pl @@ -0,0 +1,35 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've (asynchronously )?moved the new entry to the new container/i, sub {
+ my $async = $1 ? 1 : 0;
+
+ S->{'rename entry_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'rename_s';
+ my %args = ();
+
+ if ($async) {
+ $func = 'rename';
+ }
+ S->{'rename entry_async'} = $async;
+
+ $args{'-dn'} = sprintf('%s,%s,%s', $TestConfig{'data'}{'entry_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-newsuper'} = sprintf('%s,%s,%s', $TestConfig{'rename'}{'new_super'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-newrdn'} = $TestConfig{'rename'}{'new_rdn'};
+
+ S->{'rename entry_result'} = S->{'object'}->$func(%args);
+};
+
+1;
diff --git a/t/features/step_definitions/search_steps.pl b/t/features/step_definitions/search_steps.pl new file mode 100644 index 0000000..43287a7 --- /dev/null +++ b/t/features/step_definitions/search_steps.pl @@ -0,0 +1,105 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; +use Test::BDD::Cucumber::StepFile; + +our %TestConfig = %main::TestConfig; +use Data::Dumper; + +When qr/I've (asynchronously )?searched for records with scope ([^, ]+)(?:, with server control(?:s)? (.+((?:(,|and) .+)*)))?(?:, with timeout (\d+))?/, sub { + my $async = $1 ? 1 : 0; + my $scope = $2; + my $timeout = $6; + + my @server_ctrls = $3 ? map { S->{'server_controls'}{$_} } split(/\s*(?:,|and)\s*/, $3) : undef; + + my $func = "search_ext_s"; + if ($async) { + $func = "search_ext"; + } + + S->{'search_async'} = $async; + + my %args = (); + + S->{'search_result'} = S->{'object'}->$func( + -basedn => $TestConfig{'ldap'}{'base_dn'}, + -scope => S->{'object'}->$scope, + -filter => $TestConfig{'search'}{'filter'}, + -attrs => \@{['cn']}, + -attrsonly => 0, + -sctrls => [@server_ctrls], + -timeout => $timeout); +}; + +Then qr/the search count matches/, sub { + is(S->{'object'}->count_entries, $TestConfig{'search'}{'count'}, "Does the search count match?"); +}; + +Then qr/using get_all_entries for each entry returned the dn and all attributes are valid/, sub { + my $entries = S->{'object'}->get_all_entries(); + + foreach my $entry_dn (keys %{$entries}) { + isnt($entry_dn, "", "Is the dn for the entry empty?"); + + foreach my $attribute (keys %{$entries->{$entry_dn}}) { + my @vals = $entries->{$entry_dn}{$attribute}; + + ok(($#vals >= 0), "Are values returned?"); + } + } +}; + +Then qr/using (.+) for each entry returned the dn and all attributes using (.+?) are valid/, sub { + my $entry_iterate_mode = lc($1); + my $attribute_iterate_mode = lc($2); + + my $attribute_tests = sub { + my $attr = shift; + my @vals = S->{'object'}->get_values($attr); + + ok(($#vals >= 0), "Are values returned?"); + + }; + + my $attribute_block = sub { + if ($attribute_iterate_mode eq "next_attribute") { + for (my $attr = S->{'object'}->first_attribute; $attr; $attr = S->{'object'}->next_attribute) { + $attribute_tests->($attr); + } + } elsif ($attribute_iterate_mode eq "entry_attribute") { + foreach my $attr (S->{'object'}->entry_attribute) { + $attribute_tests->($attr); + } + } + }; + + my $entry_tests = sub { + isnt(S->{'object'}->get_dn(), "", "Is the dn for the entry empty?"); + }; + + if ($entry_iterate_mode eq "next_entry") { + my $ent = S->{'object'}->first_entry; + + my %ent_result = S->{'object'}->parse_result(); + S->{'cache'}{'serverctrls'} = $ent_result{'serverctrls'}; + + for (; $ent; $ent = S->{'object'}->next_entry) { + $entry_tests->($ent); + + $attribute_block->($ent); + } + } elsif ($entry_iterate_mode eq "result_entry") { + foreach my $ent (S->{'object'}->result_entry) { + $entry_tests->($ent); + + $attribute_block->($ent); + } + } + +}; + +1; diff --git a/t/features/step_definitions/server_controls_steps.pl b/t/features/step_definitions/server_controls_steps.pl new file mode 100644 index 0000000..4f27e3f --- /dev/null +++ b/t/features/step_definitions/server_controls_steps.pl @@ -0,0 +1,136 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+use Net::LDAPapi;
+use Convert::ASN1;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+Given qr/the server side sort control definition/i, sub {
+ if (!defined(S->{'asn'}{'server side sort'})) {
+ S->{'asn'}{'server side sort'} = Convert::ASN1->new;
+
+ S->{'asn'}{'server side sort'}->prepare(<<ASN) or die "prepare: ", S->{'asn'}{'server side sort'}->error;
+
+ SortKey ::= SEQUENCE {
+ attributeType OCTET STRING,
+ orderingRule [0] OCTET STRING OPTIONAL,
+ reverseOrder [1] BOOLEAN }
+
+ SortKeyList ::= SEQUENCE OF SortKey
+
+ SortResult ::= SEQUENCE {
+ sortResult ENUMERATED,
+ attributeType [0] OCTET STRING OPTIONAL }
+
+ASN
+ }
+};
+
+Given qr/the virtual list view control definition/i, sub {
+ if (!defined(S->{'asn'}{'virtual list view'})) {
+ S->{'asn'}{'virtual list view'} = Convert::ASN1->new;
+
+ S->{'asn'}{'virtual list view'}->prepare(<<ASN) or die "prepare: ", S->{'asn'}{'virtual list view'}->error;
+
+ VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER, + afterCount INTEGER, + target CHOICE { + byOffset [0] SEQUENCE { + offset INTEGER, + contentCount INTEGER + }, + greaterThanOrEqual [1] OCTET STRING + }, + contextID OCTET STRING OPTIONAL + } + + VirtualListViewResponse ::= SEQUENCE { + targetPosition INTEGER, + contentCount INTEGER, + virtualListViewResult ENUMERATED, + contextID OCTET STRING OPTIONAL + } +
+ASN
+ }
+};
+
+When qr/I've created a server side sort control/i, sub {
+ my $sss = S->{'asn'}{'server side sort'}->find('SortKeyList');
+
+ my $sss_berval = $sss->encode($TestConfig{'server_controls'}{'sss'}) or die S->{'asn'}{'server side sort'}->error;
+
+ my $sss_ctrl = S->{'object'}->create_control(
+ -oid => '1.2.840.113556.1.4.473',
+ -berval => $sss_berval,
+ );
+
+ S->{'server_controls'}{'server side sort'} = $sss_ctrl;
+};
+
+When qr/I've created a virtual list view control/i, sub {
+ my $vlv = S->{'asn'}{'virtual list view'}->find('VirtualListViewRequest');
+
+ my $vlv_berval = $vlv->encode($TestConfig{'server_controls'}{'vlv'}) or die S->{'asn'}{'virtual list view'}->error;
+
+ my $vlv_ctrl = S->{'object'}->create_control(
+ -oid => '2.16.840.1.113730.3.4.9',
+ -berval => $vlv_berval,
+ );
+
+ S->{'server_controls'}{'virtual list view'} = $vlv_ctrl;
+};
+
+Then qr/the server side sort control was successfully used/i, sub {
+ my $sss_response = S->{'asn'}{'server side sort'}->find('SortResult');
+
+ my $berval = undef;
+
+ foreach my $ctrl (@{S->{'cache'}{'serverctrls'}}) {
+ my $ctrl_oid = S->{'object'}->get_control_oid($ctrl);
+
+ if ($ctrl_oid eq '1.2.840.113556.1.4.474') {
+ $berval = S->{'object'}->get_control_berval($ctrl);
+ last;
+ }
+ }
+
+ isnt($berval, undef, "Was a berval returned?");
+
+ my $result = $sss_response->decode($berval) || ok(0, $sss_response->error);
+
+ is(ldap_err2string($result->{'sortResult'}), ldap_err2string(LDAP_SUCCESS), "Does server side sort result code match?");
+};
+
+Then qr/the virtual list view control was successfully used/i, sub {
+ my $vlv_response = S->{'asn'}{'virtual list view'}->find('VirtualListViewResponse');
+
+ my $berval = undef;
+
+ foreach my $ctrl (@{S->{'cache'}{'serverctrls'}}) {
+ my $ctrl_oid = S->{'object'}->get_control_oid($ctrl);
+
+ if ($ctrl_oid eq '2.16.840.1.113730.3.4.10') {
+ $berval = S->{'object'}->get_control_berval($ctrl);
+ last;
+ }
+ }
+
+ isnt($berval, undef, "Was a berval returned?");
+
+ my $result = $vlv_response->decode($berval) || ok(0, $vlv_response->error);
+
+ is(ldap_err2string($result->{'virtualListViewResult'}), ldap_err2string(LDAP_SUCCESS), "Does virtual list view result code match?");
+};
+
+
+1;
diff --git a/t/features/step_definitions/syncrepl_steps.pl b/t/features/step_definitions/syncrepl_steps.pl new file mode 100644 index 0000000..0725c32 --- /dev/null +++ b/t/features/step_definitions/syncrepl_steps.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Net::LDAPapi;
+use Test::More;
+use Test::BDD::Cucumber::StepFile;
+
+our %TestConfig = %main::TestConfig;
+
+use Data::Dumper;
+
+When qr/I've started listening for changes within the directory/i, sub {
+
+ S->{'listen changes_result'} = 'skipped';
+
+ return if S->{'bind_result'} eq 'skipped';
+
+ my $func = 'listen_for_changes';
+ my %args = ();
+
+ $args{'-basedn'} = sprintf('%s,%s', $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ $args{'-scope'} = LDAP_SCOPE_SUBTREE;
+ $args{'-filter'} = '(objectClass=*)';
+ $args{'-cookie'} = $TestConfig{'syncrepl'}{'cookie_dir'} . "syncrepl.$$.cookie";
+
+# open(COOKIE, ">" . $args{'-cookie'});
+# close(COOKIE);
+
+ S->{'listen changes_result'} = S->{'object'}->$func(%args);
+ S->{'object'}->next_changed_entries(S->{'listen changes_result'}, 0, 1);
+
+};
+
+Then qr/the changes were successfully notified/i, sub {
+ my $expected_container_dn = sprintf('%s,%s,%s', $TestConfig{'data'}{'container_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+ my $expected_entry_dn = sprintf('%s,%s,%s', $TestConfig{'data'}{'entry_dn'}, $TestConfig{'data'}{'test_container_dn'}, $TestConfig{'ldap'}{'base_dn'});
+
+ my $seen_expected = 0;
+
+ my $timeout_start = time();
+ my $timeout_length = 5;
+
+ while(!$seen_expected) {
+ if ((time() - $timeout_start) > $timeout_length) { last; }
+
+ while(my @entries = S->{'object'}->next_changed_entries(S->{'listen changes_result'}, 0, 1)) {
+ foreach my $entry (@entries) {
+
+ my $entry_dn = S->{'object'}->get_dn($entry->{'entry'});
+
+ if (lc($entry_dn) eq lc($expected_container_dn) || lc($entry_dn) eq lc($expected_entry_dn)) {
+ $seen_expected = 1;
+ last;
+ }
+ }
+ }
+ }
+
+ ok($seen_expected, 'Have we seen a notification for an expected DN?');
+
+ my %args;
+
+ my $asn = Convert::ASN1->new();
+ $asn->prepare(<<ASN);
+cancelRequestValue ::= SEQUENCE {
+ cancelID INTEGER
+}
+ASN
+
+ $args{'-oid'} = '1.3.6.1.1.8';
+ $args{'-result'} = \%{S->{'cancel_result code'}};
+ $args{'-berval'} = $asn->encode(cancelID => S->{'listen changes_result'});
+
+ my $cancel_status = S->{'object'}->extended_operation_s(%args);
+ is(ldap_err2string($cancel_status), ldap_err2string(LDAP_SUCCESS), 'Was cancelling the sync successful?');
+ S->{'object'}->{'entry'} = 0;
+};
+
+1;
diff --git a/t/features/step_definitions/whoami_steps.pl b/t/features/step_definitions/whoami_steps.pl new file mode 100644 index 0000000..e78bb79 --- /dev/null +++ b/t/features/step_definitions/whoami_steps.pl @@ -0,0 +1,65 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; +use Test::BDD::Cucumber::StepFile; + +our %TestConfig = %main::TestConfig; + +When qr/I\'ve (asynchronously )?queried the directory for my identity/, sub { + my $async = $1 ? 1 : 0; + + S->{'identity_authzid'} = undef; + S->{'identity_result'} = "skipped"; + + return if S->{"bind_result"} eq "skipped"; + + my $func = "whoami_s"; + my %args = (); + + if ($async) { + $func = "whoami"; + } else { + %args = ('-authzid' => \S->{'identity_authzid'}); + } + S->{'identity_async'} = $async; + + S->{'identity_result'} = S->{'object'}->$func(%args); +}; + +Then qr/the identity matches/, sub { + SKIP: { + + skip(S->{'bind_type'} . " authentication disabled in t/test-config.pl", 1) if S->{"bind_result"} eq "skipped"; + + my ($got, $expected); + + if (S->{'identity_async'}) { + $got = S->{'object'}->parse_whoami(S->{'identity_result_id'}); + } else { + $got = S->{'identity_authzid'}; + } + + S->{'identity_got'} = $got; + + if (S->{'bind_type'} eq "anonymous") { + $expected = ""; + } elsif (S->{'bind_type'} eq "simple") { + my ($attr, $value) = split(/:/, $got); + + $got = $value; + $expected = $TestConfig{'ldap'}{'bind_types'}{'simple'}{'bind_dn'}; + } elsif (S->{'bind_type'} eq "sasl") { + my ($attr, $value) = split(/:/, $got); + + $got = $value; + $expected = $TestConfig{'ldap'}{'bind_types'}{'sasl'}{'identity'}; + } + + is(lc($got), lc($expected), "Does expected identity match received identity?"); + } +}; + +1; diff --git a/t/features/syncrepl.feature b/t/features/syncrepl.feature new file mode 100644 index 0000000..3c6b9d9 --- /dev/null +++ b/t/features/syncrepl.feature @@ -0,0 +1,21 @@ +Feature: Listening for changes within the directory with syncrepl
+ As a OpenLDAP directory consumer
+ I want to ensure that I can be notified of changes to entries within the directory
+ In order to act quickly on changes
+
+ Background:
+ Given a usable Net::LDAPapi class
+
+ Scenario: Can listen for changes within the directory
+ Given a Net::LDAPapi object that has been connected to the LDAP server
+ When I've bound with default authentication to the directory
+ And a test container has been created
+ And I've started listening for changes within the directory
+ And I've added a new entry to the directory
+ And I've added a new container to the directory
+ And I've deleted the new entry from the directory
+ Then the new entry result is LDAP_SUCCESS
+ And the new container result is LDAP_SUCCESS
+ And the delete entry result is LDAP_SUCCESS
+ And the changes were successfully notified
+ And the test container has been deleted
diff --git a/t/features/whoami.feature b/t/features/whoami.feature new file mode 100644 index 0000000..892fa04 --- /dev/null +++ b/t/features/whoami.feature @@ -0,0 +1,61 @@ +Feature: Querying the directory for my identity + As a directory consumer + I want to ensure that I can retrieve my identity + In order to determine my DN when using a non-simple authentication + + Background: + Given a usable Net::LDAPapi class + + Scenario: Can query identity with anonymous authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with anonymous authentication to the directory + And I've queried the directory for my identity + Then the bind result is LDAP_SUCCESS + And the identity result is LDAP_SUCCESS + And the identity matches + + Scenario: Can query identity with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've bound with simple authentication to the directory + And I've queried the directory for my identity + Then the bind result is LDAP_SUCCESS + And the identity result is LDAP_SUCCESS + And the identity matches + + Scenario: Can query identity with sasl authentication + Given a Net::LDAPapi object that has been connected to the ldapi LDAP server + When I've bound with sasl authentication to the directory + And I've queried the directory for my identity + Then the bind result is LDAP_SUCCESS + And the identity result is LDAP_SUCCESS + And the identity matches + + Scenario: Can asynchronously query identity with anonymous authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with anonymous authentication to the directory + And I've asynchronously queried the directory for my identity + Then the bind result message type is LDAP_RES_BIND + And the bind result is LDAP_SUCCESS + And after waiting for all results, the identity result message type is LDAP_RES_EXTENDED + And the identity result is LDAP_SUCCESS + And the identity matches + + Scenario: Can asynchronously query identity with simple authentication + Given a Net::LDAPapi object that has been connected to the LDAP server + When I've asynchronously bound with simple authentication to the directory + And I've asynchronously queried the directory for my identity + Then the bind result message type is LDAP_RES_BIND + And the bind result is LDAP_SUCCESS + And after waiting for all results, the identity result message type is LDAP_RES_EXTENDED + And the identity result is LDAP_SUCCESS + And the identity matches + +# Scenario: Can asynchronously query identity with sasl authentication +# Given a Net::LDAPapi object that has been connected to the ldapi LDAP server +# When I've asynchronously bound with sasl authentication to the directory +# And I've asynchronously queried the directory for my identity +# Then the bind result message type is LDAP_RES_BIND +# And the bind result is LDAP_SUCCESS +# And after waiting for all results, the identity result message type is LDAP_RES_EXTENDED +# And the identity result is LDAP_SUCCESS +# And the identity matches diff --git a/t/test-config.pl b/t/test-config.pl new file mode 100644 index 0000000..83fa8e6 --- /dev/null +++ b/t/test-config.pl @@ -0,0 +1,129 @@ +# Developer tests require: +# Test::More +# Test::BDD::Cucumber +our $RunDeveloperTests = 0; + +# Default config. +# If you're a developer of Net::LDAPapi or are likely to have multiple trees that share common test config, +# then you should override in ~/.net-ldapapi-test-config.conf (See below) +our %TestConfig = ( + 'ldap' => { + 'server' => { + 'tcp' => { + '-host' => 'localhost', + '-port' => 389, + }, + 'ldapi' => { + '-url' => 'ldapi:///', + '-debug' => 1 + } + }, + 'base_dn' => 'dc=example,dc=com', + 'bind_types' => { + 'anonymous' => { + 'enabled' => 1, + }, + 'simple' => { + 'enabled' => 1, + 'bind_dn' => 'cn=admin,dc=example,dc=com', + 'bind_pw' => 'password', + }, + 'sasl' => { + 'enabled' => 1, + 'sasl_parms' => { + '-mech' => 'EXTERNAL', + }, + 'identity' => "gidNumber=" . $< . "+uidNumber=" . (split(/ /, "$("))[0] . ",cn=peercred,cn=external,cn=auth" + } + }, + 'default_server' => 'tcp', + 'default_bind_type' => 'simple', + }, + 'search' => { + 'filter' => "sn=Last", + 'count' => 1, + }, + 'data' => { + 'test_container_attributes' => { + 'objectClass' => ['top', 'organizationalUnit'], + 'ou' => 'Test Container', + }, + 'container_attributes' => { + 'objectClass' => ['top', 'organizationalUnit'], + 'ou' => 'Test - Add Container', + }, + 'entry_attributes' => { + 'objectClass' => ['top', 'person' ,'organizationalPerson', 'inetOrgPerson'], + 'cn' => 'Test - Add Entry', + 'sn' => 'Entry', + 'givenName' => 'Test - Add', + }, + 'test_container_dn' => 'ou=Test Container', + 'container_dn' => 'ou=Test - Add Container', + 'entry_dn' => 'cn=Test - Add Entry', + }, + 'rename' => { + 'dn' => 'cn=Test - Add Entry', + 'new_rdn' => 'cn=Test - Add Entry', + 'new_super' => 'ou=Test - Add Container' + }, + 'modify' => { + 'new_attribute' => { + 'title' => { 'a' => ['New Test Title'] } + }, + 'modify_attribute' => { + 'title' => { 'r' => ['Modified Test Title'] } + }, + 'remove_attribute' => { + 'title' => '' + }, + }, + 'syncrepl' => { + 'enabled' => 1, + 'cookie_dir' => '/tmp/' + }, + 'server_controls' => { + 'sss' => [ + { + 'attributeType' => 'sn', + 'orderingRule' => '2.5.13.3', + 'reverseOrder' => 1 + }, + ], + 'vlv' => { + 'beforeCount' => 0, + 'afterCount' => 3, + 'target' => { + 'byOffset' => { + 'offset' => 1, + 'contentCount' => 0 + } + } + }, + }, + 'compare' => { + 'entry_attribute' => 'cn', + 'compare_attribute' => 'ou' + } +); + + +# Allow overrides from outside the source tree. +# This is a standard Perl file. Example below. +if ( -e $ENV{'HOME'} . '/.net-ldapapi-test-config.conf') { + require $ENV{'HOME'} . '/.net-ldapapi-test-config.conf'; +} + +1; +__END__ + +$RunDeveloperTests = 1; + +$TestConfig{'ldap'}{'base_dn'} = "o=Test Data,c=NZ"; +$TestConfig{'ldap'}{'bind_types'}{'simple'}{'bind_dn'} = "cn=admin,o=Test Data,c=NZ"; +$TestConfig{'ldap'}{'bind_types'}{'simple'}{'bind_pw'} = "password"; + +$TestConfig{'search'}{'filter'} = "sn=O'Donnell"; + + +1; |