unit tests; consistency with the other OpenStack services
diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go
index 5e9256d..f823b3d 100644
--- a/openstack/blockstorage/v1/snapshots/requests.go
+++ b/openstack/blockstorage/v1/snapshots/requests.go
@@ -1,9 +1,11 @@
 package snapshots
 
 import (
-	"github.com/racker/perigee"
 	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/openstack/utils"
+	"github.com/rackspace/gophercloud/pagination"
+
+	"github.com/racker/perigee"
 )
 
 type CreateOpts struct {
@@ -14,7 +16,7 @@
 	VolumeID    string
 }
 
-func Create(client *gophercloud.ServiceClient, opts CreateOpts) (*Snapshot, error) {
+func Create(client *gophercloud.ServiceClient, opts *CreateOpts) CreateResult {
 	type snapshot struct {
 		Description *string                `json:"display_description,omitempty"`
 		Force       bool                   `json:"force,omitempty"`
@@ -37,38 +39,86 @@
 
 	reqBody.Snapshot.Force = opts.Force
 
-	type response struct {
-		Snapshot Snapshot `json:"snapshot"`
-	}
-
-	var respBody response
-
-	_, err := perigee.Request("POST", snapshotsURL(client), perigee.Options{
+	var res CreateResult
+	_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{200, 201},
 		ReqBody:     &reqBody,
-		Results:     &respBody,
+		Results:     &res.Resp,
 	})
-	if err != nil {
-		return nil, err
-	}
-
-	return &respBody.Snapshot, nil
+	return res
 }
 
-func Delete(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("Delete", snapshotURL(client, id), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
 	})
-	return err
+	return res
 }
 
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var gr GetResult
-	_, err := perigee.Request("GET", snapshotURL(client, id), perigee.Options{
-		Results:     &gr.r,
+	var res GetResult
+	_, res.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
+		Results:     &res.Resp,
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{200},
 	})
-	gr.err = err
-	return gr
+	return res
+}
+
+type ListOpts struct {
+	Name     string `q:"display_name"`
+	Status   string `q:"status"`
+	VolumeID string `q:"volume_id"`
+}
+
+func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
+	url := listURL(client)
+	if opts != nil {
+		query, err := gophercloud.BuildQueryString(opts)
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query.String()
+	}
+
+	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+		return ListResult{pagination.SinglePageBase(r)}
+	}
+	return pagination.NewPager(client, url, createPage)
+}
+
+type UpdateOpts struct {
+	Description string
+	Name        string
+}
+
+func Update(client *gophercloud.ServiceClient, id string, opts *UpdateOpts) UpdateResult {
+	type update struct {
+		Description *string `json:"display_description,omitempty"`
+		Name        *string `json:"display_name,omitempty"`
+	}
+
+	type request struct {
+		Volume update `json:"snapshot"`
+	}
+
+	reqBody := request{
+		Volume: update{},
+	}
+
+	reqBody.Volume.Description = utils.MaybeString(opts.Description)
+	reqBody.Volume.Name = utils.MaybeString(opts.Name)
+
+	var res UpdateResult
+
+	_, res.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{200},
+		ReqBody:     &reqBody,
+		Results:     &res.Resp,
+	})
+	return res
 }
diff --git a/openstack/blockstorage/v1/snapshots/requests_test.go b/openstack/blockstorage/v1/snapshots/requests_test.go
index 1e77fbd..dae53fd 100644
--- a/openstack/blockstorage/v1/snapshots/requests_test.go
+++ b/openstack/blockstorage/v1/snapshots/requests_test.go
@@ -1 +1,160 @@
 package snapshots
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const TokenID = "123"
+
+func ServiceClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{
+			TokenID: TokenID,
+		},
+		Endpoint: th.Endpoint(),
+	}
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+		{
+			"snapshots": [
+				{
+					"id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+					"display_name": "snapshot-001"
+				},
+				{
+					"id": "96c3bda7-c82a-4f50-be73-ca7621794835",
+					"display_name": "snapshot-002"
+				}
+			]
+		}
+		`)
+	})
+
+	client := ServiceClient()
+	count := 0
+
+	List(client, &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractSnapshots(page)
+		if err != nil {
+			t.Errorf("Failed to extract snapshots: %v", err)
+			return false, err
+		}
+
+		expected := []Snapshot{
+			Snapshot{
+				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+				Name: "snapshot-001",
+			},
+			Snapshot{
+				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
+				Name: "snapshot-002",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+{
+    "snapshot": {
+        "display_name": "snapshot-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+			`)
+	})
+
+	v, err := Get(ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, v.Name, "snapshot-001")
+	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "snapshot": {
+        "display_name": "snapshot-001"
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "snapshot": {
+        "display_name": "snapshot-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+		`)
+	})
+
+	options := &CreateOpts{Name: "snapshot-001"}
+	n, err := Create(ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Name, "snapshot-001")
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := Delete(ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go
index d8178c1..cc3a419 100644
--- a/openstack/blockstorage/v1/snapshots/results.go
+++ b/openstack/blockstorage/v1/snapshots/results.go
@@ -3,37 +3,82 @@
 import (
 	"fmt"
 
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+
 	"github.com/mitchellh/mapstructure"
 )
 
 type Snapshot struct {
-	CreatedAt   string
-	Description string
-	ID          string
-	Metadata    map[string]interface{}
-	Name        string
-	Size        int
-	Status      string
-	VolumeID    string
+	Status           string            `mapstructure:"status"`
+	Name             string            `mapstructure:"display_name"`
+	Attachments      []string          `mapstructure:"attachments"`
+	AvailabilityZone string            `mapstructure:"availability_zone"`
+	Bootable         string            `mapstructure:"bootable"`
+	CreatedAt        string            `mapstructure:"created_at"`
+	Description      string            `mapstructure:"display_discription"`
+	VolumeType       string            `mapstructure:"volume_type"`
+	SnapshotID       string            `mapstructure:"snapshot_id"`
+	SourceVolID      string            `mapstructure:"source_volid"`
+	Metadata         map[string]string `mapstructure:"metadata"`
+	ID               string            `mapstructure:"id"`
+	Size             int               `mapstructure:"size"`
 }
 
-type GetResult struct {
-	err error
-	r   map[string]interface{}
+// ListResult is a *http.Response that is returned from a call to the List function.
+type ListResult struct {
+	pagination.SinglePageBase
 }
 
-func (gr GetResult) ExtractSnapshot() (*Snapshot, error) {
-	if gr.err != nil {
-		return nil, gr.err
+// IsEmpty returns true if a ListResult contains no container names.
+func (r ListResult) IsEmpty() (bool, error) {
+	volumes, err := ExtractSnapshots(r)
+	if err != nil {
+		return true, err
+	}
+	return len(volumes) == 0, nil
+}
+
+// ExtractSnapshots extracts and returns the Volumes from a 'List' request.
+func ExtractSnapshots(page pagination.Page) ([]Snapshot, error) {
+	var response struct {
+		Snapshots []Snapshot `json:"snapshots"`
 	}
 
-	var response struct {
+	err := mapstructure.Decode(page.(ListResult).Body, &response)
+	return response.Snapshots, err
+}
+
+type commonResult struct {
+	gophercloud.CommonResult
+}
+
+// Extract returns a pointer to the Snapshot from a commonResult.Resp.
+func (r commonResult) Extract() (*Snapshot, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
 		Snapshot *Snapshot `json:"snapshot"`
 	}
 
-	err := mapstructure.Decode(gr.r, &response)
+	err := mapstructure.Decode(r.Resp, &res)
 	if err != nil {
-		return nil, fmt.Errorf("snapshots: Error decoding snapshot.GetResult: %v", err)
+		return nil, fmt.Errorf("snapshots: Error decoding snapshots.commonResult: %v", err)
 	}
-	return response.Snapshot, nil
+	return res.Snapshot, nil
 }
+
+type GetResult struct {
+	commonResult
+}
+
+type CreateResult struct {
+	commonResult
+}
+type UpdateResult struct {
+	commonResult
+}
+
+type DeleteResult commonResult
diff --git a/openstack/blockstorage/v1/snapshots/urls.go b/openstack/blockstorage/v1/snapshots/urls.go
index c129185..fb324bb 100644
--- a/openstack/blockstorage/v1/snapshots/urls.go
+++ b/openstack/blockstorage/v1/snapshots/urls.go
@@ -2,10 +2,26 @@
 
 import "github.com/rackspace/gophercloud"
 
-func snapshotsURL(c *gophercloud.ServiceClient) string {
+func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("snapshots")
 }
 
-func snapshotURL(c *gophercloud.ServiceClient, id string) string {
+func deleteURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("snapshots", id)
 }
+
+func getURL(c *gophercloud.ServiceClient, id string) string {
+	return deleteURL(c, id)
+}
+
+func listURL(c *gophercloud.ServiceClient) string {
+	return createURL(c)
+}
+
+func metadataURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL("snapshots", id, "metadata")
+}
+
+func updateURL(c *gophercloud.ServiceClient, id string) string {
+	return metadataURL(c, id)
+}
diff --git a/openstack/blockstorage/v1/snapshots/urls_test.go b/openstack/blockstorage/v1/snapshots/urls_test.go
index c36227f..8877580 100644
--- a/openstack/blockstorage/v1/snapshots/urls_test.go
+++ b/openstack/blockstorage/v1/snapshots/urls_test.go
@@ -13,14 +13,38 @@
 	return &gophercloud.ServiceClient{Endpoint: endpoint}
 }
 
-func TestSnapshotsURL(t *testing.T) {
-	actual := snapshotsURL(endpointClient())
+func TestCreateURL(t *testing.T) {
+	actual := createURL(endpointClient())
 	expected := endpoint + "snapshots"
 	th.AssertEquals(t, expected, actual)
 }
 
-func TestSnapshotURL(t *testing.T) {
-	actual := snapshotURL(endpointClient(), "foo")
+func TestDeleteURL(t *testing.T) {
+	actual := deleteURL(endpointClient(), "foo")
 	expected := endpoint + "snapshots/foo"
 	th.AssertEquals(t, expected, actual)
 }
+
+func TestGetURL(t *testing.T) {
+	actual := getURL(endpointClient(), "foo")
+	expected := endpoint + "snapshots/foo"
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestListURL(t *testing.T) {
+	actual := listURL(endpointClient())
+	expected := endpoint + "snapshots"
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestMetadataURL(t *testing.T) {
+	actual := metadataURL(endpointClient(), "foo")
+	expected := endpoint + "snapshots/foo/metadata"
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestUpdateURL(t *testing.T) {
+	actual := updateURL(endpointClient(), "foo")
+	expected := endpoint + "snapshots/foo/metadata"
+	th.AssertEquals(t, expected, actual)
+}