Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 89 additions & 10 deletions core-integration-test/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,89 @@
openstack4j-integrationtest
===========================

Integration tests for openstack4j's identity (keystone) v3 written in groovy using spock testframework and betamax to record/replay server-client communication
Integration tests for OpenStack4j written in [Groovy](http://www.groovy-lang.org/) using [Spock](http://spockframework.org/) test-framework and [Betamax](https://github.com/betamaxteam/betamax) to record/replay server-client communication.

HOW TO
------

### Write Integration Tests (IT)

Betamax is used to record and replay client-server communication to/from a tape.
Therefore it is necessary to setup a RecorderRule, which specifies in which folder the tape can be found (tapeRoot),
some match rules (e.g. by request/response method, path, query parameters) and a default mode for the tape which should always be read & write.
Each test method that wants to use the recordings needs to specify a tape via the annotation `@Betamax(tape="<tapeName>")`.

Here are the basics of how a Specification should look like:
```
@Slf4j
class KeystoneRegionServiceSpec extends AbstractSpec {

// setup test name
@Rule TestName KeystoneRegionServiceTest

// setup recorder rule used by Betamax
@Rule public RecorderRule recorderRule = new RecorderRule(
Configuration.builder()
.tapeRoot(new File(TAPEROOT + "identity.v3"))
.defaultMatchRules(MatchRules.method, MatchRules.path, MatchRules.queryParams)
.defaultMode(TapeMode.READ_WRITE)
.build());;

// the following logic ensures all required attributes are set
static final boolean skipTest

static {
if(
USER_ID == null ||
AUTH_URL == null ||
PASSWORD == null ||
DOMAIN_ID == null ) {

skipTest = true
}
else{
skipTest = false
}
}

def setupSpec() {

if( skipTest != true ) {
log.info("USER_ID: " + USER_ID)
log.info("AUTH_URL: " + AUTH_URL)
log.info("PASSWORD: " + PASSWORD)
log.info("DOMAIN_ID: " + DOMAIN_ID)
log.info("PROJECT_ID: " + PROJECT_ID)
}
else {
log.warn("Skipping integration-test cases because not all mandatory attributes are set.")
}
}

def setup() {
log.info("-> Test: '$KeystoneRegionServiceTest.methodName'")
}

@IgnoreIf({ skipTest })

// specify the tape name
@Betamax(tape="regionService_all.tape")
def "region service CRUD test cases"() {

// spock test cases

}
}
```


### Run ITests

* Integration tests are executed in the integration-test phase. Do a ```mvn clean integration-test``` or ```mvn clean verify``` (or any phase later) to trigger the tests.

### Write ITests

* Using the Spock Framework: http://spockframework.github.io/spock/docs/1.1-rc-1/index.html
* Recording cloud interactions with Betamax: https://github.com/betamaxteam/betamax
#### Recording

Important
---------
* the tests work completely without setting the following environment variables. In which case the tapes are used in **playback** mode.
* Adding or changing test cases with cloud interaction requires **recording** by providing the following required parameters in your system environment. They are similar to the ones the python openstack-client needs. Make sure they are set or tests will be skipped.
In order to record the communication to the tapes the following environment attributes must be set:
* attributes used: (incomplete)
* OS_AUTH_URL = ``` "http://<fqdn>:5000/v3" ```
* OS_USER_ID
Expand All @@ -28,4 +94,17 @@ Important
* OS_PROJECT_DOMAIN_ID
* OS_PROJECT_DOMAIN_NAME
* OS_DOMAIN_ID
* OS_DOMAIN_NAME
* OS_DOMAIN_NAME

Afterwards these values in the tapes must be replaced using the default ones defined in the AbstractSpec.

#### Playback

In playback mode the recordings are used. Make sure no OS_*-attributes are set.


Important
---------

In case of `com.fasterxml.jackson.databind.JsonMappingException: Unexpected end-of-input in VALUE_STRING`
remove the Content-Length header from the recording. Use this regex for search & replace: `^\s*Content-Length: '[0-9]*'\R` .
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.openstack4j.api

import java.nio.file.Paths
import groovy.util.logging.Slf4j
import org.openstack4j.core.transport.Config
import org.openstack4j.core.transport.ProxyHost
import org.openstack4j.core.transport.internal.HttpExecutor
import spock.lang.Specification

import java.nio.file.Paths

@Slf4j
abstract class AbstractSpec extends Specification {

Expand All @@ -28,15 +29,6 @@ abstract class AbstractSpec extends Specification {
def static String PROJECT_DOMAIN_ID = System.getenv('OS_PROJECT_DOMAIN_ID') ?: DOMAIN_ID
def static String REGION_ONE = System.getenv('OS_REGION_NAME') ?: 'europe'

// add. attr. req. by KeystoneUserServiceSpec
def static String ANOTHER_GROUP_ID = System.getenv('OS_ANOTHER_GROUP_ID') ?: 'd26804e7813b4dcd9712781832f1fac1'

// add. attr. req. by KeystoneRoleServiceSpec
def static String ROLE_CRUD_USER_ID = System.getenv('OS_ROLE_CRUD_USER_ID') ?: '7174b49851d64276b72601c67ce347e2' // another user used only for role tests
def static String ROLE_CRUD_GROUP_ID = System.getenv('OS_ROLE_CRUD_GROUP_ID') ?: '8ffc0b29008b436b92e114ab27df0288' // another group used only for role tests
def static String ROLE_CRUD_ROLE_ID = System.getenv('OS_ROLE_CRUD_ROLE_ID') ?: 'd163f290c7b24e568098e75f7ab4b89a'
def static String ROLE_CRUD_ANOTHER_ROLE_ID = System.getenv('OS_ROLE_CRUD_ANOTHER_ROLE_ID') ?: '9ab55538924d45588fdb62e0e5fcbc40'

def setupSpec() {
log.info("Using connector: " + HttpExecutor.create().getExecutorName())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.openstack4j.api.identity.v3

import groovy.util.logging.Slf4j

import org.junit.Rule
import org.junit.rules.TestName

import org.openstack4j.api.AbstractSpec
import org.openstack4j.api.OSClient.OSClientV3
import org.openstack4j.api.exceptions.RegionEndpointNotFoundException
Expand All @@ -13,22 +13,23 @@ import org.openstack4j.model.identity.AuthVersion
import org.openstack4j.model.identity.v3.User
import org.openstack4j.openstack.OSFactory

import spock.lang.IgnoreIf
import software.betamax.Configuration
import software.betamax.MatchRules
import software.betamax.junit.RecorderRule
import software.betamax.TapeMode
import software.betamax.junit.Betamax
import software.betamax.junit.RecorderRule

import spock.lang.IgnoreIf

@Slf4j
class KeystoneAuthenticationSpec extends AbstractSpec {

@Rule TestName KeystoneAuthenticationTest
@Rule public RecorderRule recorder = new RecorderRule(
@Rule public RecorderRule recorderRule = new RecorderRule(
Configuration.builder()
.tapeRoot(new File(TAPEROOT + "identity.v3"))
.defaultMatchRules(MatchRules.method, MatchRules.path, MatchRules.queryParams)
.defaultMode(TapeMode.READ_WRITE)
.build());

static final boolean skipTest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
package org.openstack4j.api.identity


import groovy.util.logging.Slf4j
import org.junit.Rule
import org.junit.rules.TestName

import org.openstack4j.api.AbstractSpec
import org.openstack4j.api.OSClient.OSClientV3
import org.openstack4j.model.common.Identifier
import org.openstack4j.model.common.ActionResponse
import org.openstack4j.model.common.Identifier
import org.openstack4j.model.identity.v3.Credential
import org.openstack4j.model.identity.v3.User
import org.openstack4j.openstack.OSFactory

import spock.lang.IgnoreIf
import software.betamax.Configuration
import software.betamax.MatchRules
import software.betamax.junit.RecorderRule
import software.betamax.TapeMode
import software.betamax.junit.Betamax
import software.betamax.junit.RecorderRule

import spock.lang.IgnoreIf

@Slf4j
class KeystoneCredentialServiceSpec extends AbstractSpec {

@Rule TestName KeystoneCredentialServiceTest
@Rule public RecorderRule recorder = new RecorderRule(
@Rule public RecorderRule recorderRule = new RecorderRule(
Configuration.builder()
.tapeRoot(new File(TAPEROOT + "identity.v3"))
.defaultMatchRules(MatchRules.method, MatchRules.path, MatchRules.queryParams)
.defaultMode(TapeMode.READ_WRITE)
.build());

// additional attributes for credential tests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package org.openstack4j.api.identity.v3

import groovy.util.logging.Slf4j

import org.junit.Rule
import org.junit.rules.TestName

import org.openstack4j.api.AbstractSpec
import org.openstack4j.api.Builders
import org.openstack4j.api.OSClient.OSClientV3
import org.openstack4j.model.common.Identifier
import org.openstack4j.model.common.ActionResponse
import org.openstack4j.model.common.Identifier
import org.openstack4j.model.identity.v3.Domain
import org.openstack4j.openstack.OSFactory

import spock.lang.IgnoreIf
import software.betamax.Configuration
import software.betamax.MatchRules
import software.betamax.junit.RecorderRule
import software.betamax.TapeMode
import software.betamax.junit.Betamax
import software.betamax.junit.RecorderRule

import spock.lang.IgnoreIf

@Slf4j
class KeystoneDomainServiceSpec extends AbstractSpec {

@Rule TestName KeystoneDomainServiceTest
@Rule public RecorderRule recorder = new RecorderRule(
@Rule public RecorderRule recorderRule = new RecorderRule(
Configuration.builder()
.tapeRoot(new File(TAPEROOT + "identity.v3"))
.defaultMatchRules(MatchRules.method, MatchRules.path, MatchRules.queryParams)
.defaultMode(TapeMode.READ_WRITE)
.build());

// used for domain crud tests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
package org.openstack4j.api.identity.v3

import groovy.util.logging.Slf4j

import org.junit.Rule
import org.junit.rules.TestName

import org.openstack4j.api.AbstractSpec
import org.openstack4j.api.Builders
import org.openstack4j.api.OSClient.OSClientV3
import org.openstack4j.model.common.ActionResponse
import org.openstack4j.model.common.Identifier
import org.openstack4j.model.identity.v3.Group
import org.openstack4j.model.identity.v3.User
import org.openstack4j.model.common.ActionResponse
import org.openstack4j.openstack.OSFactory

import spock.lang.IgnoreIf
import software.betamax.Configuration
import software.betamax.MatchRules
import software.betamax.junit.RecorderRule
import software.betamax.TapeMode
import software.betamax.junit.Betamax
import software.betamax.junit.RecorderRule

import spock.lang.IgnoreIf

@Slf4j
class KeystoneGroupServiceSpec extends AbstractSpec {

@Rule TestName KeystoneGroupServiceTest
@Rule public RecorderRule recorder = new RecorderRule(
@Rule public RecorderRule recorderRule = new RecorderRule(
Configuration.builder()
.tapeRoot(new File(TAPEROOT + "identity.v3"))
.defaultMatchRules(MatchRules.method, MatchRules.path, MatchRules.queryParams)
.defaultMode(TapeMode.READ_WRITE)
.build());

// additional attributes for group service tests
Expand All @@ -43,31 +44,29 @@ class KeystoneGroupServiceSpec extends AbstractSpec {
static final boolean skipTest

static {
if(
if (
USER_ID == null ||
AUTH_URL == null ||
PASSWORD == null ||
DOMAIN_ID == null ||
USER_DOMAIN_ID == null ) {
AUTH_URL == null ||
PASSWORD == null ||
DOMAIN_ID == null ||
USER_DOMAIN_ID == null) {

skipTest = false
}
else{
} else {
skipTest = false
}
}

// run before the first feature method; similar to JUnit's @BeforeClass
def setupSpec() {

if( skipTest != true ) {
if (skipTest != true) {
log.info("USER_ID: " + USER_ID)
log.info("AUTH_URL: " + AUTH_URL)
log.info("PASSWORD: " + PASSWORD)
log.info("DOMAIN_ID: " + DOMAIN_ID)
log.info("USER_DOMAIN_ID: " + USER_DOMAIN_ID)
}
else {
} else {
log.warn("Skipping integration-test cases because not all mandatory attributes are set.")
}
}
Expand All @@ -76,11 +75,10 @@ class KeystoneGroupServiceSpec extends AbstractSpec {
log.info("-> Test: '$KeystoneGroupServiceTest.methodName'")
}


// ------------ GroupService Tests ------------

@IgnoreIf({ skipTest })
@Betamax(tape="groupService_group_crud.tape")
@Betamax(tape = "groupService_group_crud.tape")
def "create, read, update, delete group-service test cases"() {

given: "authenticated OSClient"
Expand Down Expand Up @@ -205,12 +203,12 @@ class KeystoneGroupServiceSpec extends AbstractSpec {
//
// then: "we should no longer find the group used in this scenario"
// groupList.contains(group) == false

when: "non-existent group is listed by name and domain id"
Group nonExistent_group_byName_byDomainId = os.identity().groups().getByName("nonExistentGroup", DOMAIN_ID)

then: "the return value should be null"
nonExistent_group_byName_byDomainId == null
when: "non-existent group is listed by name and domain id"
Group nonExistent_group_byName_byDomainId = os.identity().groups().getByName("nonExistentGroup", DOMAIN_ID)

then: "the return value should be null"
nonExistent_group_byName_byDomainId == null

cleanup: "we delete the user used in this scenario"
ActionResponse response_deleteUser_success = os.identity().users().delete(GROUP_CRUD_USER_ID)
Expand Down
Loading