blob: 27f3fdcad3b979c45cbca9ea915194ba9dda1eac [file] [log] [blame]
package testing
import (
"fmt"
"net/http"
"testing"
"time"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
"github.com/gophercloud/gophercloud/testhelper"
)
// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, requestJSON string) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{},
Endpoint: testhelper.Endpoint(),
}
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
testhelper.TestMethod(t, r, "POST")
testhelper.TestHeader(t, r, "Content-Type", "application/json")
testhelper.TestHeader(t, r, "Accept", "application/json")
testhelper.TestJSONRequest(t, r, requestJSON)
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{
"token": {
"expires_at": "2014-10-02T13:45:00.000000Z"
}
}`)
})
if scope != nil {
options.Scope = *scope
}
_, err := tokens.Create(&client, &options).Extract()
if err != nil {
t.Errorf("Create returned an error: %v", err)
}
}
func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{},
Endpoint: testhelper.Endpoint(),
}
if includeToken {
client.TokenID = "abcdef123456"
}
if scope != nil {
options.Scope = *scope
}
_, err := tokens.Create(&client, &options).Extract()
if err == nil {
t.Errorf("Create did NOT return an error")
}
if err != expectedErr {
t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
}
}
func TestCreateUserIDAndPassword(t *testing.T) {
authTokenPost(t, tokens.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": { "id": "me", "password": "squirrel!" }
}
}
}
}
`)
}
func TestCreateUsernameDomainIDPassword(t *testing.T) {
authTokenPost(t, tokens.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"domain": {
"id": "abc123"
},
"name": "fakey",
"password": "notpassword"
}
}
}
}
}
`)
}
func TestCreateUsernameDomainNamePassword(t *testing.T) {
authTokenPost(t, tokens.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"domain": {
"name": "spork.net"
},
"name": "frank",
"password": "swordfish"
}
}
}
}
}
`)
}
func TestCreateTokenID(t *testing.T) {
authTokenPost(t, tokens.AuthOptions{TokenID: "12345abcdef"}, nil, `
{
"auth": {
"identity": {
"methods": ["token"],
"token": {
"id": "12345abcdef"
}
}
}
}
`)
}
func TestCreateProjectIDScope(t *testing.T) {
options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
scope := &tokens.Scope{ProjectID: "123456"}
authTokenPost(t, options, scope, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"id": "fenris",
"password": "g0t0h311"
}
}
},
"scope": {
"project": {
"id": "123456"
}
}
}
}
`)
}
func TestCreateDomainIDScope(t *testing.T) {
options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
scope := &tokens.Scope{DomainID: "1000"}
authTokenPost(t, options, scope, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"id": "fenris",
"password": "g0t0h311"
}
}
},
"scope": {
"domain": {
"id": "1000"
}
}
}
}
`)
}
func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"}
authTokenPost(t, options, scope, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"id": "fenris",
"password": "g0t0h311"
}
}
},
"scope": {
"project": {
"domain": {
"id": "1000"
},
"name": "world-domination"
}
}
}
}
`)
}
func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
authTokenPost(t, options, scope, `
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"id": "fenris",
"password": "g0t0h311"
}
}
},
"scope": {
"project": {
"domain": {
"name": "evil-plans"
},
"name": "world-domination"
}
}
}
}
`)
}
func TestCreateExtractsTokenFromResponse(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{},
Endpoint: testhelper.Endpoint(),
}
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Subject-Token", "aaa111")
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{
"token": {
"expires_at": "2014-10-02T13:45:00.000000Z"
}
}`)
})
options := tokens.AuthOptions{UserID: "me", Password: "shhh"}
token, err := tokens.Create(&client, &options).Extract()
if err != nil {
t.Fatalf("Create returned an error: %v", err)
}
if token.ID != "aaa111" {
t.Errorf("Expected token to be aaa111, but was %s", token.ID)
}
}
func TestCreateFailureEmptyAuth(t *testing.T) {
authTokenPostErr(t, tokens.AuthOptions{}, nil, false, gophercloud.ErrMissingPassword{})
}
func TestCreateFailureTokenIDUsername(t *testing.T) {
authTokenPostErr(t, tokens.AuthOptions{Username: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUsernameWithToken{})
}
func TestCreateFailureTokenIDUserID(t *testing.T) {
authTokenPostErr(t, tokens.AuthOptions{UserID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUserIDWithToken{})
}
func TestCreateFailureTokenIDDomainID(t *testing.T) {
authTokenPostErr(t, tokens.AuthOptions{DomainID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainIDWithToken{})
}
func TestCreateFailureTokenIDDomainName(t *testing.T) {
authTokenPostErr(t, tokens.AuthOptions{DomainName: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainNameWithToken{})
}
func TestCreateFailureMissingUser(t *testing.T) {
options := tokens.AuthOptions{Password: "supersecure"}
authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{})
}
func TestCreateFailureBothUser(t *testing.T) {
options := tokens.AuthOptions{
Password: "supersecure",
Username: "oops",
UserID: "redundancy",
}
authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{})
}
func TestCreateFailureMissingDomain(t *testing.T) {
options := tokens.AuthOptions{
Password: "supersecure",
Username: "notuniqueenough",
}
authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{})
}
func TestCreateFailureBothDomain(t *testing.T) {
options := tokens.AuthOptions{
Password: "supersecure",
Username: "someone",
DomainID: "hurf",
DomainName: "durf",
}
authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{})
}
func TestCreateFailureUserIDDomainID(t *testing.T) {
options := tokens.AuthOptions{
UserID: "100",
Password: "stuff",
DomainID: "oops",
}
authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDWithUserID{})
}
func TestCreateFailureUserIDDomainName(t *testing.T) {
options := tokens.AuthOptions{
UserID: "100",
Password: "sssh",
DomainName: "oops",
}
authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainNameWithUserID{})
}
func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{ProjectName: "notenough"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{})
}
func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDOrProjectName{})
}
func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{ProjectID: "toomuch", DomainID: "notneeded"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{})
}
func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{ProjectID: "toomuch", DomainName: "notneeded"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{})
}
func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{DomainID: "toomuch", DomainName: "notneeded"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{})
}
func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{DomainName: "notenough"}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainName{})
}
/*
func TestCreateFailureEmptyScope(t *testing.T) {
options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
scope := &tokens.Scope{}
authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeEmpty{})
}
*/
func TestGetRequest(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{
TokenID: "12345abcdef",
},
Endpoint: testhelper.Endpoint(),
}
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
testhelper.TestMethod(t, r, "GET")
testhelper.TestHeader(t, r, "Content-Type", "")
testhelper.TestHeader(t, r, "Accept", "application/json")
testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{ "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } }
`)
})
token, err := tokens.Get(&client, "abcdef12345").Extract()
if err != nil {
t.Errorf("Info returned an error: %v", err)
}
expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014")
if token.ExpiresAt != gophercloud.JSONRFC3339Milli(expected) {
t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), time.Time(token.ExpiresAt).Format(time.UnixDate))
}
}
func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient {
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{
TokenID: "12345abcdef",
},
Endpoint: testhelper.Endpoint(),
}
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
testhelper.TestMethod(t, r, expectedMethod)
testhelper.TestHeader(t, r, "Content-Type", "")
testhelper.TestHeader(t, r, "Accept", "application/json")
testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
w.WriteHeader(status)
})
return client
}
func TestValidateRequestSuccessful(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent)
ok, err := tokens.Validate(&client, "abcdef12345")
if err != nil {
t.Errorf("Unexpected error from Validate: %v", err)
}
if !ok {
t.Errorf("Validate returned false for a valid token")
}
}
func TestValidateRequestFailure(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound)
ok, err := tokens.Validate(&client, "abcdef12345")
if err != nil {
t.Errorf("Unexpected error from Validate: %v", err)
}
if ok {
t.Errorf("Validate returned true for an invalid token")
}
}
func TestValidateRequestError(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := prepareAuthTokenHandler(t, "HEAD", http.StatusUnauthorized)
_, err := tokens.Validate(&client, "abcdef12345")
if err == nil {
t.Errorf("Missing expected error from Validate")
}
}
func TestRevokeRequestSuccessful(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
res := tokens.Revoke(&client, "abcdef12345")
testhelper.AssertNoErr(t, res.Err)
}
func TestRevokeRequestError(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
res := tokens.Revoke(&client, "abcdef12345")
if res.Err == nil {
t.Errorf("Missing expected error from Revoke")
}
}
func TestNoTokenInResponse(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
client := gophercloud.ServiceClient{
ProviderClient: &gophercloud.ProviderClient{},
Endpoint: testhelper.Endpoint(),
}
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{}`)
})
options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"}
_, err := tokens.Create(&client, &options).Extract()
if err == nil {
t.Error("Create succeeded with no token returned")
}
}