diff --git a/tests/modules/core/lib/Auth/Process/AttributeAddTest.php b/tests/modules/core/lib/Auth/Process/AttributeAddTest.php
index 20748fafe0116b0e5c047a28aae45978d31241eb..95733aeafec69cda1c64db3745de68f6bd6579dc 100644
--- a/tests/modules/core/lib/Auth/Process/AttributeAddTest.php
+++ b/tests/modules/core/lib/Auth/Process/AttributeAddTest.php
@@ -6,7 +6,7 @@
 class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
 {
 
-    /*
+    /**
      * Helper function to run the filter with a given configuration.
      *
      * @param array $config  The filter configuration.
@@ -20,7 +20,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         return $request;
     }
 
-    /*
+    /**
      * Test the most basic functionality.
      */
     public function testBasic()
@@ -37,7 +37,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['test'], array('value1', 'value2'));
     }
 
-    /*
+    /**
      * Test that existing attributes are left unmodified.
      */
     public function testExistingNotModified()
@@ -61,7 +61,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['original2'], array('original_value2'));
     }
 
-    /*
+    /**
      * Test single string as attribute value.
      */
     public function testStringValue()
@@ -78,8 +78,8 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['test'], array('value'));
     }
 
-    /*
-     * Test the most basic functionality.
+    /**
+     * Test adding multiple attributes in one config.
      */
     public function testAddMultiple()
     {
@@ -98,7 +98,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['test2'], array('value2'));
     }
 
-    /*
+    /**
      * Test behavior when appending attribute values.
      */
     public function testAppend()
@@ -116,7 +116,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['test'], array('value1', 'value2'));
     }
 
-    /*
+    /**
      * Test replacing attribute values.
      */
     public function testReplace()
@@ -135,4 +135,60 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase
         $this->assertEquals($attributes['test'], array('value2'));
     }
 
+    /**
+     * Test wrong usage generates exceptions
+     *
+     * @expectedException Exception
+     */
+    public function testWrongFlag()
+    {
+        $config = array(
+            '%nonsense',
+            'test' => array('value2'),
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('value1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+    }
+
+    /**
+     * Test wrong attribute name
+     *
+     * @expectedException Exception
+     */
+    public function testWrongAttributeName()
+    {
+        $config = array(
+            '%replace',
+            array('value2'),
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('value1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+    }
+
+    /**
+     * Test wrong attribute value
+     *
+     * @expectedException Exception
+     */
+    public function testWrongAttributeValue()
+    {
+        $config = array(
+            '%replace',
+            'test' => array(true),
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('value1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+    }
 }
diff --git a/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..29e696400417a31d7e0b4631e3fb946c9db80ce8
--- /dev/null
+++ b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * Test for the core:AttributeCopy filter.
+ */
+class Test_Core_Auth_Process_AttributeCopy extends PHPUnit_Framework_TestCase
+{
+
+    /**
+     * Helper function to run the filter with a given configuration.
+     *
+     * @param array $config  The filter configuration.
+     * @param array $request  The request state.
+     * @return array  The state array after processing.
+     */
+    private static function processFilter(array $config, array $request)
+    {
+        $filter = new sspmod_core_Auth_Process_AttributeCopy($config, NULL);
+        $filter->process($request);
+        return $request;
+    }
+
+    /**
+     * Test the most basic functionality.
+     */
+    public function testBasic()
+    {
+        $config = array(
+            'test' => 'testnew',
+        );
+        $request = array(
+            'Attributes' => array('test' => array('AAP')),
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertArrayHasKey('test', $attributes);
+        $this->assertArrayHasKey('testnew', $attributes);
+        $this->assertEquals($attributes['testnew'], array('AAP'));
+    }
+
+    /**
+     * Test that existing attributes are left unmodified.
+     */
+    public function testExistingNotModified()
+    {
+        $config = array(
+            'test' => 'testnew',
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('AAP'),
+                'original1' => array('original_value1'),
+                'original2' => array('original_value2'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertArrayHasKey('testnew', $attributes);
+        $this->assertEquals($attributes['test'], array('AAP'));
+        $this->assertArrayHasKey('original1', $attributes);
+        $this->assertEquals($attributes['original1'], array('original_value1'));
+        $this->assertArrayHasKey('original2', $attributes);
+        $this->assertEquals($attributes['original2'], array('original_value2'));
+    }
+
+    /**
+     * Test copying multiple attributes
+     */
+    public function testCopyMultiple()
+    {
+        $config = array(
+            'test1' => 'new1',
+            'test2' => 'new2',
+        );
+        $request = array(
+            'Attributes' => array('test1' => array('val1'), 'test2' => array('val2.1','val2.2')),
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertArrayHasKey('new1', $attributes);
+        $this->assertEquals($attributes['new1'], array('val1'));
+        $this->assertArrayHasKey('new2', $attributes);
+        $this->assertEquals($attributes['new2'], array('val2.1','val2.2'));
+    }
+
+    /**
+     * Test behaviour when target attribute exists (should be replaced).
+     */
+    public function testCopyClash()
+    {
+        $config = array(
+            'test' => 'new1',
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('testvalue1'),
+                'new1' => array('newvalue1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertEquals($attributes['new1'], array('testvalue1'));
+    }
+
+    /**
+     * Test wrong attribute name
+     *
+     * @expectedException Exception
+     */
+    public function testWrongAttributeName()
+    {
+        $config = array(
+            array('value2'),
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('value1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+    }
+
+    /**
+     * Test wrong attribute value
+     *
+     * @expectedException Exception
+     */
+    public function testWrongAttributeValue()
+    {
+        $config = array(
+            'test' => array('test2'),
+        );
+        $request = array(
+            'Attributes' => array(
+                'test' => array('value1'),
+            ),
+        );
+        $result = self::processFilter($config, $request);
+    }
+}
diff --git a/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php b/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef3873ea264d8209d9b2f793148cb68406234b0c
--- /dev/null
+++ b/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * Test for the core:ScopeAttribute filter.
+ */
+class Test_Core_Auth_Process_ScopeAttribute extends PHPUnit_Framework_TestCase
+{
+
+    /*
+     * Helper function to run the filter with a given configuration.
+     *
+     * @param array $config  The filter configuration.
+     * @param array $request  The request state.
+     * @return array  The state array after processing.
+     */
+    private static function processFilter(array $config, array $request)
+    {
+        $filter = new sspmod_core_Auth_Process_ScopeAttribute($config, NULL);
+        $filter->process($request);
+        return $request;
+    }
+
+    /*
+     * Test the most basic functionality.
+     */
+    public function testBasic()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'eduPersonPrincipalName' => array('jdoe@example.com'),
+                'eduPersonAffiliation' => array('member'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertArrayHasKey('eduPersonScopedAffiliation', $attributes);
+        $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@example.com'));
+    }
+
+    /*
+     * If scope already set, module must add, not overwrite.
+     */
+    public function testNoOverwrite()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'eduPersonPrincipalName' => array('jdoe@example.com'),
+                'eduPersonAffiliation' => array('member'),
+                'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('library-walk-in@example.edu', 'member@example.com'));
+    }
+
+    /*
+     * If source attribute not set, nothing happens
+     */
+    public function testNoSourceAttribute()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'mail' => array('j.doe@example.edu', 'john@example.org'),
+                'eduPersonAffiliation' => array('member'),
+                'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $this->assertEquals($request['Attributes'], $result['Attributes']);
+    }
+
+    /*
+     * If scope attribute not set, nothing happens
+     */
+    public function testNoScopeAttribute()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'mail' => array('j.doe@example.edu', 'john@example.org'),
+                'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'),
+                'eduPersonPrincipalName' => array('jdoe@example.com'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $this->assertEquals($request['Attributes'], $result['Attributes']);
+    }
+
+    /*
+     * When multiple @ signs in attribute, will use the first one.
+     */
+    public function testMultiAt()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'eduPersonPrincipalName' => array('john@doe@example.com'),
+                'eduPersonAffiliation' => array('member'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@doe@example.com'));
+    }
+
+    /*
+     * When multiple values in source attribute, should render multiple targets.
+     */
+    public function testMultivaluedSource()
+    {
+        $config = array(
+            'scopeAttribute' => 'eduPersonPrincipalName',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'eduPersonPrincipalName' => array('jdoe@example.com'),
+                'eduPersonAffiliation' => array('member','staff','faculty'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@example.com','staff@example.com','faculty@example.com'));
+    }
+
+    /*
+     * When the source attribute doesn't have a scope, the entire value is used.
+     */
+    public function testNoAt()
+    {
+        $config = array(
+            'scopeAttribute' => 'schacHomeOrganization',
+            'sourceAttribute' => 'eduPersonAffiliation',
+            'targetAttribute' => 'eduPersonScopedAffiliation',
+        );
+        $request = array(
+            'Attributes' => array(
+                'schacHomeOrganization' => array('example.org'),
+                'eduPersonAffiliation' => array('student'),
+            )
+        );
+        $result = self::processFilter($config, $request);
+        $attributes = $result['Attributes'];
+        $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('student@example.org'));
+    }
+}
diff --git a/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php b/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php
index 955d908c10ecd1b61d54b4fb8b0aef9a1be9f27f..f6ef1bf90414abb094f6e92db22c2d75fda23fd1 100644
--- a/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php
+++ b/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php
@@ -103,7 +103,7 @@ class Test_Core_Auth_Process_ScopeFromAttribute extends PHPUnit_Framework_TestCa
      * NOTE: currently disabled: this triggers a warning and a warning
      * wants to start a session which we cannot do in phpunit. How to fix?
      */
-/*    public function testNoAt()
+    public function testNoAt()
     {
         $config = array(
             'sourceAttribute' => 'eduPersonPrincipalName',
@@ -118,5 +118,5 @@ class Test_Core_Auth_Process_ScopeFromAttribute extends PHPUnit_Framework_TestCa
         $attributes = $result['Attributes'];
 
         $this->assertArrayNotHasKey('scope', $attributes);
-    } */
+    }
 }