...

Source file src/github.com/go-kivik/kivik/v4/pouchdb/test/test.go

Documentation: github.com/go-kivik/kivik/v4/pouchdb/test

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    12  
    13  //go:build js
    14  
    15  // Package test provides PouchDB integration tests.
    16  package test
    17  
    18  import (
    19  	"net/http"
    20  	"net/url"
    21  	"os"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/gopherjs/gopherjs/js"
    27  
    28  	kivik "github.com/go-kivik/kivik/v4"
    29  	"github.com/go-kivik/kivik/v4/kiviktest"
    30  	"github.com/go-kivik/kivik/v4/kiviktest/kt"
    31  )
    32  
    33  func init() {
    34  	if pouchDB := js.Global.Get("PouchDB"); pouchDB != js.Undefined {
    35  		memPouch := js.Global.Get("PouchDB").Call("defaults", map[string]interface{}{
    36  			"db": js.Global.Call("require", "memdown"),
    37  		})
    38  		js.Global.Set("PouchDB", memPouch)
    39  	}
    40  }
    41  
    42  // RegisterPouchDBSuites registers the PouchDB test suites.
    43  func RegisterPouchDBSuites() {
    44  	kiviktest.RegisterSuite(kiviktest.SuitePouchLocal, kt.SuiteConfig{
    45  		"PreCleanup.skip": true,
    46  
    47  		// Features which are not supported by PouchDB
    48  		"Log.skip":         true,
    49  		"Flush.skip":       true,
    50  		"Security.skip":    true, // FIXME: Perhaps implement later with a plugin?
    51  		"SetSecurity.skip": true, // FIXME: Perhaps implement later with a plugin?
    52  		"DBUpdates.skip":   true,
    53  
    54  		"AllDBs.skip":   true, // FIXME: Find a way to test with the plugin
    55  		"CreateDB.skip": true, // FIXME: No way to validate if this works unless/until allDbs works
    56  		"DBExists.skip": true, // FIXME: Maybe fix this if/when allDBs works?
    57  
    58  		"AllDocs/Admin.databases":                        []string{},
    59  		"AllDocs/RW/group/Admin/WithDocs/UpdateSeq.skip": true,
    60  
    61  		"Find/Admin.databases": []string{},
    62  		// TODO: Fix this and uncomment https://github.com/go-kivik/kivik/issues/588
    63  		// "Find/RW/group/Admin/Warning.warning": indexWarning,
    64  
    65  		"Explain.databases": []string{},
    66  		"Explain.plan": &kivik.QueryPlan{
    67  			Index: map[string]interface{}{
    68  				"ddoc": nil,
    69  				"name": "_all_docs",
    70  				"type": "special",
    71  				"def":  map[string]interface{}{"fields": []interface{}{map[string]string{"_id": "asc"}}},
    72  			},
    73  			Selector: map[string]interface{}{"_id": map[string]interface{}{"$gt": nil}},
    74  			Options: map[string]interface{}{
    75  				"bookmark":  "nil",
    76  				"conflicts": false,
    77  				"r":         []int{49},
    78  				"sort":      map[string]interface{}{},
    79  				"use_index": []interface{}{},
    80  			},
    81  			Fields: func() []interface{} {
    82  				if ver := runtime.Version(); strings.HasPrefix(ver, "go1.16") {
    83  					return []interface{}{}
    84  				}
    85  				// From GopherJS 17 on, null arrays are properly converted to nil
    86  				return nil
    87  			}(),
    88  			Range: map[string]interface{}{
    89  				"start_key": nil,
    90  			},
    91  		},
    92  
    93  		"Query/RW/group/Admin/WithDocs/UpdateSeq.skip": true,
    94  
    95  		"Version.version":        `^[789]\.\d\.\d$`,
    96  		"Version.vendor":         `^PouchDB$`,
    97  		"Version.vendor_version": `^[789]\.\d\.\d$`,
    98  
    99  		"Get/RW/group/Admin/bogus.status":  http.StatusNotFound,
   100  		"Get/RW/group/NoAuth/bogus.status": http.StatusNotFound,
   101  
   102  		"GetRev/RW/group/Admin/bogus.status":  http.StatusNotFound,
   103  		"GetRev/RW/group/NoAuth/bogus.status": http.StatusNotFound,
   104  
   105  		"Delete/RW/Admin/group/MissingDoc.status":       http.StatusNotFound,
   106  		"Delete/RW/Admin/group/InvalidRevFormat.status": http.StatusBadRequest,
   107  		"Delete/RW/Admin/group/WrongRev.status":         http.StatusConflict,
   108  
   109  		"Stats/Admin.skip": true, // No predefined DBs for Local PouchDB
   110  
   111  		"BulkDocs/RW/Admin/group/Mix/Conflict.status": http.StatusConflict,
   112  
   113  		"GetAttachment/RW/group/Admin/foo/NotFound.status": http.StatusNotFound,
   114  
   115  		"GetAttachmentMeta/RW/group/Admin/foo/NotFound.status": http.StatusNotFound,
   116  
   117  		"PutAttachment/RW/group/Admin/Conflict.status": http.StatusConflict,
   118  
   119  		// "DeleteAttachment/RW/group/Admin/NotFound.status": http.StatusNotFound, // https://github.com/pouchdb/pouchdb/issues/6409
   120  		"DeleteAttachment/RW/group/Admin/NoDoc.status": http.StatusNotFound,
   121  
   122  		"Put/RW/Admin/group/LeadingUnderscoreInID.status": http.StatusBadRequest,
   123  		"Put/RW/Admin/group/Conflict.status":              http.StatusConflict,
   124  
   125  		"CreateIndex/RW/Admin/group/EmptyIndex.status":   http.StatusInternalServerError,
   126  		"CreateIndex/RW/Admin/group/BlankIndex.status":   http.StatusBadRequest,
   127  		"CreateIndex/RW/Admin/group/InvalidIndex.status": http.StatusInternalServerError,
   128  		"CreateIndex/RW/Admin/group/NilIndex.status":     http.StatusInternalServerError,
   129  		"CreateIndex/RW/Admin/group/InvalidJSON.status":  http.StatusBadRequest,
   130  
   131  		"GetIndexes.databases": []string{},
   132  
   133  		"DeleteIndex/RW/Admin/group/NotFoundDdoc.status": http.StatusNotFound,
   134  		"DeleteIndex/RW/Admin/group/NotFoundName.status": http.StatusNotFound,
   135  
   136  		"Replicate.skip": true, // No need to do this for both Local and Remote
   137  
   138  		"Query/RW/group/Admin/WithoutDocs/ScanDoc.status": http.StatusBadRequest,
   139  
   140  		"Changes/Continuous.options": kivik.Params(map[string]interface{}{
   141  			"live":    true,
   142  			"timeout": false,
   143  		}),
   144  	})
   145  	kiviktest.RegisterSuite(kiviktest.SuitePouchRemote, kt.SuiteConfig{
   146  		// Features which are not supported by PouchDB
   147  		"Log.skip":         true,
   148  		"Flush.skip":       true,
   149  		"Session.skip":     true,
   150  		"Security.skip":    true, // FIXME: Perhaps implement later with a plugin?
   151  		"SetSecurity.skip": true, // FIXME: Perhaps implement later with a plugin?
   152  		"DBUpdates.skip":   true,
   153  
   154  		"PreCleanup.skip": true,
   155  
   156  		"AllDBs.skip": true, // FIXME: Perhaps a workaround can be found?
   157  
   158  		"CreateDB/RW/NoAuth.status":         http.StatusUnauthorized,
   159  		"CreateDB/RW/Admin/Recreate.status": http.StatusPreconditionFailed,
   160  
   161  		"DBExists.databases":              []string{"_users", "chicken"},
   162  		"DBExists/Admin/_users.exists":    true,
   163  		"DBExists/Admin/chicken.exists":   false,
   164  		"DBExists/NoAuth/_users.exists":   true,
   165  		"DBExists/NoAuth/chicken.exists":  false,
   166  		"DBExists/RW/group/Admin.exists":  true,
   167  		"DBExists/RW/group/NoAuth.exists": true,
   168  
   169  		"DestroyDB/RW/NoAuth/NonExistantDB.status": http.StatusNotFound,
   170  		"DestroyDB/RW/Admin/NonExistantDB.status":  http.StatusNotFound,
   171  		"DestroyDB/RW/NoAuth/ExistingDB.status":    http.StatusUnauthorized,
   172  
   173  		"AllDocs.databases":                                  []string{"_replicator", "_users", "chicken"},
   174  		"AllDocs/Admin/_replicator.expected":                 []string{"_design/_replicator"},
   175  		"AllDocs/Admin/_replicator.offset":                   0,
   176  		"AllDocs/Admin/_users.expected":                      []string{"_design/_auth"},
   177  		"AllDocs/Admin/chicken.status":                       http.StatusNotFound,
   178  		"AllDocs/NoAuth/_replicator.status":                  http.StatusUnauthorized,
   179  		"AllDocs/NoAuth/_users.status":                       http.StatusUnauthorized,
   180  		"AllDocs/NoAuth/chicken.status":                      http.StatusNotFound,
   181  		"AllDocs/Admin/_replicator/WithDocs/UpdateSeq.skip":  true,
   182  		"AllDocs/Admin/_users/WithDocs/UpdateSeq.skip":       true,
   183  		"AllDocs/RW/group/Admin/WithDocs/UpdateSeq.skip":     true,
   184  		"AllDocs/RW/group/Admin/WithoutDocs/UpdateSeq.skip":  true,
   185  		"AllDocs/RW/group/NoAuth/WithDocs/UpdateSeq.skip":    true,
   186  		"AllDocs/RW/group/NoAuth/WithoutDocs/UpdateSeq.skip": true,
   187  
   188  		"Find.databases":             []string{"chicken", "_duck"},
   189  		"Find/Admin/chicken.status":  http.StatusNotFound,
   190  		"Find/Admin/_duck.status":    http.StatusNotFound,
   191  		"Find/NoAuth/chicken.status": http.StatusNotFound,
   192  		"Find/NoAuth/_duck.status":   http.StatusUnauthorized,
   193  		// TODO: Fix this and uncomment https://github.com/go-kivik/kivik/issues/588
   194  		// "Find/RW/group/Admin/Warning.warning":  "No matching index found, create an index to optimize query time",
   195  		"Find/RW/group/NoAuth/Warning.warning": "No matching index found, create an index to optimize query time",
   196  
   197  		"Explain.databases":             []string{"chicken", "_duck"},
   198  		"Explain/Admin/chicken.status":  http.StatusNotFound,
   199  		"Explain/Admin/_duck.status":    http.StatusNotFound,
   200  		"Explain/NoAuth/chicken.status": http.StatusNotFound,
   201  		"Explain/NoAuth/_duck.status":   http.StatusUnauthorized,
   202  		"Explain.plan": &kivik.QueryPlan{
   203  			Index: map[string]interface{}{
   204  				"ddoc": nil,
   205  				"name": "_all_docs",
   206  				"type": "special",
   207  				"def":  map[string]interface{}{"fields": []interface{}{map[string]string{"_id": "asc"}}},
   208  			},
   209  			Selector: map[string]interface{}{"_id": map[string]interface{}{"$gt": nil}},
   210  			Options: map[string]interface{}{
   211  				"bookmark":        "nil",
   212  				"conflicts":       false,
   213  				"execution_stats": false,
   214  				"r":               []int{49},
   215  				"sort":            map[string]interface{}{},
   216  				"use_index":       []interface{}{},
   217  				"stable":          false,
   218  				"stale":           false,
   219  				"update":          true,
   220  				"skip":            0,
   221  				"limit":           25, //nolint:gomnd
   222  				"fields":          "all_fields",
   223  			},
   224  			Fields: func() []interface{} {
   225  				if ver := runtime.Version(); strings.HasPrefix(ver, "go1.16") {
   226  					return []interface{}{}
   227  				}
   228  				// From GopherJS 17 on, null arrays are properly converted to nil
   229  				return nil
   230  			}(),
   231  			Range: nil,
   232  			Limit: 25, //nolint:gomnd
   233  		},
   234  
   235  		"CreateIndex/RW/Admin/group/EmptyIndex.status":    http.StatusBadRequest,
   236  		"CreateIndex/RW/Admin/group/BlankIndex.status":    http.StatusBadRequest,
   237  		"CreateIndex/RW/Admin/group/InvalidIndex.status":  http.StatusBadRequest,
   238  		"CreateIndex/RW/Admin/group/NilIndex.status":      http.StatusBadRequest,
   239  		"CreateIndex/RW/Admin/group/InvalidJSON.status":   http.StatusBadRequest,
   240  		"CreateIndex/RW/NoAuth/group/EmptyIndex.status":   http.StatusBadRequest,
   241  		"CreateIndex/RW/NoAuth/group/BlankIndex.status":   http.StatusBadRequest,
   242  		"CreateIndex/RW/NoAuth/group/InvalidIndex.status": http.StatusBadRequest,
   243  		"CreateIndex/RW/NoAuth/group/NilIndex.status":     http.StatusBadRequest,
   244  		"CreateIndex/RW/NoAuth/group/InvalidJSON.status":  http.StatusBadRequest,
   245  		"CreateIndex/RW/NoAuth/group/Valid.status":        http.StatusInternalServerError, // COUCHDB-3374
   246  
   247  		"GetIndexes.databases":                     []string{"_replicator", "_users", "_global_changes"},
   248  		"GetIndexes/Admin/_replicator.indexes":     []kivik.Index{kt.AllDocsIndex},
   249  		"GetIndexes/Admin/_users.indexes":          []kivik.Index{kt.AllDocsIndex},
   250  		"GetIndexes/Admin/_global_changes.indexes": []kivik.Index{kt.AllDocsIndex},
   251  		"GetIndexes/NoAuth/_replicator.indexes":    []kivik.Index{kt.AllDocsIndex},
   252  		"GetIndexes/NoAuth/_users.indexes":         []kivik.Index{kt.AllDocsIndex},
   253  		"GetIndexes/NoAuth/_global_changes.skip":   true, // Pouch connects to the DB before searching the Index, so this test fails
   254  		"GetIndexes/NoAuth/_global_changes.status": http.StatusUnauthorized,
   255  		"GetIndexes/RW.indexes": []kivik.Index{
   256  			kt.AllDocsIndex,
   257  			{
   258  				DesignDoc: "_design/foo",
   259  				Name:      "bar",
   260  				Type:      "json",
   261  				Definition: map[string]interface{}{
   262  					"fields": []map[string]string{
   263  						{"foo": "asc"},
   264  					},
   265  					"partial_filter_selector": map[string]string{},
   266  				},
   267  			},
   268  		},
   269  
   270  		"DeleteIndex/RW/Admin/group/NotFoundDdoc.status":  http.StatusNotFound,
   271  		"DeleteIndex/RW/Admin/group/NotFoundName.status":  http.StatusNotFound,
   272  		"DeleteIndex/RW/NoAuth/group/NotFoundDdoc.status": http.StatusNotFound,
   273  		"DeleteIndex/RW/NoAuth/group/NotFoundName.status": http.StatusNotFound,
   274  
   275  		"Query/RW/group/Admin/WithDocs/UpdateSeq.skip":  true,
   276  		"Query/RW/group/NoAuth/WithDocs/UpdateSeq.skip": true,
   277  
   278  		"Version.version":        `^[789]\.\d\.\d$`,
   279  		"Version.vendor":         `^PouchDB$`,
   280  		"Version.vendor_version": `^[789]\.\d\.\d$`,
   281  
   282  		"Get/RW/group/Admin/bogus.status":  http.StatusNotFound,
   283  		"Get/RW/group/NoAuth/bogus.status": http.StatusNotFound,
   284  
   285  		"GetRev/RW/group/Admin/bogus.status":  http.StatusNotFound,
   286  		"GetRev/RW/group/NoAuth/bogus.status": http.StatusNotFound,
   287  
   288  		"Delete/RW/Admin/group/MissingDoc.status":        http.StatusNotFound,
   289  		"Delete/RW/Admin/group/InvalidRevFormat.status":  http.StatusBadRequest,
   290  		"Delete/RW/Admin/group/WrongRev.status":          http.StatusConflict,
   291  		"Delete/RW/NoAuth/group/MissingDoc.status":       http.StatusNotFound,
   292  		"Delete/RW/NoAuth/group/InvalidRevFormat.status": http.StatusBadRequest,
   293  		"Delete/RW/NoAuth/group/WrongRev.status":         http.StatusConflict,
   294  		"Delete/RW/NoAuth/group/DesignDoc.status":        http.StatusUnauthorized,
   295  
   296  		"Stats.databases":             []string{"_users", "chicken"},
   297  		"Stats/Admin/chicken.status":  http.StatusNotFound,
   298  		"Stats/NoAuth/chicken.status": http.StatusNotFound,
   299  
   300  		"BulkDocs/RW/NoAuth/group/Mix/Conflict.status": http.StatusConflict,
   301  		"BulkDocs/RW/Admin/group/Mix/Conflict.status":  http.StatusConflict,
   302  
   303  		"GetAttachment/RW/group/Admin/foo/NotFound.status":  http.StatusNotFound,
   304  		"GetAttachment/RW/group/NoAuth/foo/NotFound.status": http.StatusNotFound,
   305  
   306  		"GetAttachmentMeta/RW/group/Admin/foo/NotFound.status":  http.StatusNotFound,
   307  		"GetAttachmentMeta/RW/group/NoAuth/foo/NotFound.status": http.StatusNotFound,
   308  
   309  		"PutAttachment/RW/group/Admin/Conflict.status":         http.StatusConflict,
   310  		"PutAttachment/RW/group/NoAuth/Conflict.status":        http.StatusConflict,
   311  		"PutAttachment/RW/group/NoAuth/UpdateDesignDoc.status": http.StatusUnauthorized,
   312  		"PutAttachment/RW/group/NoAuth/CreateDesignDoc.status": http.StatusUnauthorized,
   313  
   314  		// "DeleteAttachment/RW/group/Admin/NotFound.status":  http.StatusNotFound, // COUCHDB-3362
   315  		// "DeleteAttachment/RW/group/NoAuth/NotFound.status": http.StatusNotFound, // COUCHDB-3362
   316  		"DeleteAttachment/RW/group/Admin/NoDoc.status":      http.StatusConflict,
   317  		"DeleteAttachment/RW/group/NoAuth/NoDoc.status":     http.StatusConflict,
   318  		"DeleteAttachment/RW/group/NoAuth/DesignDoc.status": http.StatusUnauthorized,
   319  
   320  		"Put/RW/Admin/group/LeadingUnderscoreInID.status":  http.StatusBadRequest,
   321  		"Put/RW/Admin/group/Conflict.status":               http.StatusConflict,
   322  		"Put/RW/NoAuth/group/DesignDoc.status":             http.StatusUnauthorized,
   323  		"Put/RW/NoAuth/group/LeadingUnderscoreInID.status": http.StatusBadRequest,
   324  		"Put/RW/NoAuth/group/Conflict.status":              http.StatusConflict,
   325  
   326  		"Replicate.NotFoundDB": func() string {
   327  			var dsn string
   328  			for _, env := range []string{
   329  				"KIVIK_TESt_DSN_COUCH22", "KIVIK_TEST_DSN_COUCH21",
   330  				"KIVIK_TEST_DSN_COUCH20", "KIVIK_TEST_DSN_COUCH16",
   331  				"KIVIK_TEST_DSN_CLOUDANT",
   332  			} {
   333  				dsn = os.Getenv(env)
   334  				if dsn != "" {
   335  					break
   336  				}
   337  			}
   338  			parsed, _ := url.Parse(dsn)
   339  			parsed.User = nil
   340  			return strings.TrimSuffix(parsed.String(), "/") + "/doesntexist"
   341  		}(),
   342  		"Replicate.prefix":         "none",
   343  		"Replicate.timeoutSeconds": 5, //nolint:gomnd
   344  		"Replicate.mode":           "pouchdb",
   345  		"Replicate/RW/Admin/group/MissingSource/Results.status":  http.StatusUnauthorized,
   346  		"Replicate/RW/Admin/group/MissingTarget/Results.status":  http.StatusUnauthorized,
   347  		"Replicate/RW/NoAuth/group/MissingSource/Results.status": http.StatusUnauthorized,
   348  		"Replicate/RW/NoAuth/group/MissingTarget/Results.status": http.StatusUnauthorized,
   349  
   350  		"Query/RW/group/Admin/WithoutDocs/ScanDoc.status":  http.StatusBadRequest,
   351  		"Query/RW/group/NoAuth/WithoutDocs/ScanDoc.status": http.StatusBadRequest,
   352  
   353  		// "ViewCleanup/RW/NoAuth.status": http.StatusUnauthorized, # FIXME: #14
   354  
   355  		"Changes/Continuous.options": kivik.Params(map[string]interface{}{
   356  			"live":    true,
   357  			"timeout": false,
   358  		}),
   359  	})
   360  }
   361  
   362  // PouchLocalTest runs the PouchDB tests against a local database.
   363  func PouchLocalTest(t *testing.T) {
   364  	client, err := kivik.New("pouch", "")
   365  	if err != nil {
   366  		t.Errorf("Failed to connect to PouchDB driver: %s", err)
   367  		return
   368  	}
   369  	clients := &kt.Context{
   370  		RW:    true,
   371  		Admin: client,
   372  		T:     t,
   373  	}
   374  	kiviktest.RunTestsInternal(clients, kiviktest.SuitePouchLocal)
   375  }
   376  
   377  // PouchRemoteTest runs the PouchDB tests against a remote CouchDB database.
   378  func PouchRemoteTest(t *testing.T) {
   379  	kiviktest.DoTest(t, kiviktest.SuitePouchRemote, "KIVIK_TEST_DSN_COUCH22")
   380  }
   381  

View as plain text