1

I've been dealing with a set of failures where a VCR-enabled test adds an additional record to my Projects table, and that record persists across additional tests, causing failures, rather than being rolled back at the end of the test. Specifically, I have 4 Projects created by my Rails fixtures, and then a test that includes a VCR cassette to add an additional project. That test always passes, but the database does not reset to the 4 projects afterwards.

Conveniently, the project is open source, so you can run it yourself. The bug can be seen on master at https://github.com/linuxfoundation/cii-best-practices-badge (checkout commit 048776f so as not to get a temporary workaround) . The test causing the problem is https://github.com/linuxfoundation/cii-best-practices-badge/blob/master/test/features/github_login_test.rb which (with a VCR cassette) logs into Github and fetches the list of public projects from a test user. It is using Capybara via the https://github.com/blowmage/minitest-rails-capybara gem.

When run with SEED=41779 rake test, the VCR-using test succeeds, but a later test fails because it counts 5 rather than 4 projects.

Here is the test log, where you can see that a ROLLBACK is called at the end but a SAVEPOINT was never specified at the beginning of the test.

---------------------------------------------------
GithubLoginTest: test_0001_Has link to GitHub Login
---------------------------------------------------
Started GET "/" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by StaticPagesController#home as HTML
  Rendered static_pages/home.html.erb within layouts/application (0.2ms)
  Rendered layouts/_header.html.erb (0.5ms)
  Rendered layouts/_footer.html.erb (1.3ms)
Completed 200 OK in 6ms (Views: 4.7ms | ActiveRecord: 0.0ms)
Started GET "/projects/new" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by ProjectsController#new as HTML
  Rendered sessions/new.html.erb (3.2ms)
  Rendered projects/new.html.erb within layouts/application (8.6ms)
  Rendered layouts/_header.html.erb (1.5ms)
  Rendered layouts/_footer.html.erb (1.9ms)
Completed 200 OK in 28ms (Views: 25.0ms | ActiveRecord: 0.0ms)
Started POST "/auth/github" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Started GET "/auth/github/callback" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by SessionsController#create as HTML
  Parameters: {"provider"=>"github"}
  [1m[36mUser Load (0.3ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2 LIMIT 1[0m  [["provider", "github"], ["uid", "12345"]]
  [1m[35m (0.1ms)[0m  BEGIN
  [1m[36mSQL (0.5ms)[0m  [1mINSERT INTO "users" ("provider", "uid", "name", "email", "nickname", "activated", "created_at", "updated_at", "activation_digest") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"[0m  [["provider", "github"], ["uid", "12345"], ["name", "CII Test"], ["email", "test@example.com"], ["nickname", "CIITheRobot"], ["activated", "t"], ["created_at", "2016-05-31 02:02:03.288717"], ["updated_at", "2016-05-31 02:02:03.288717"], ["activation_digest", "$2a$04$BhFbVTS54Yeeurh83VBmdOs.T30jvliDmEFIQ6/lMpnhpN7rGvVTW"]]
  [1m[35m (0.3ms)[0m  COMMIT
Redirected to http://127.0.0.1:31337/
  [1m[36mUser Load (0.3ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m  [["id", 764040364]]
Completed 302 Found in 11ms (ActiveRecord: 1.5ms)
Started GET "/" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by StaticPagesController#home as HTML
  [1m[35mUser Load (0.2ms)[0m  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 764040364]]
  Rendered static_pages/home.html.erb within layouts/application (0.2ms)
  Rendered layouts/_header.html.erb (0.6ms)
  Rendered layouts/_footer.html.erb (1.2ms)
Completed 200 OK in 6ms (Views: 4.3ms | ActiveRecord: 0.2ms)
Started GET "/projects/new" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by ProjectsController#new as HTML
  [1m[36mUser Load (0.2ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m  [["id", 764040364]]
  Rendered shared/_error_messages.html.erb (0.6ms)
  Rendered projects/new.html.erb within layouts/application (31.6ms)
  Rendered layouts/_header.html.erb (1.0ms)
  Rendered layouts/_footer.html.erb (1.8ms)
Completed 200 OK in 41ms (Views: 38.6ms | ActiveRecord: 0.2ms)
Started POST "/projects" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by ProjectsController#create as HTML
  Parameters: {"utf8"=>"✓", "project"=>{"repo_url"=>"https://github.com/ciitest/cii-best-practices-badge"}, "commit"=>"Submit GitHub Repository"}
  [1m[35mUser Load (0.4ms)[0m  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 764040364]]
  [1m[36mProject Exists (0.6ms)[0m  [1mSELECT  1 AS one FROM "projects" WHERE "projects"."repo_url" = $1 LIMIT 1[0m  [["repo_url", "https://github.com/ciitest/cii-best-practices-badge"]]
  [1m[35m (0.2ms)[0m  BEGIN
  [1m[36mProject Exists (0.6ms)[0m  [1mSELECT  1 AS one FROM "projects" WHERE "projects"."repo_url" = 'https://github.com/ciitest/cii-best-practices-badge' LIMIT 1[0m
  [1m[35mSQL (1.0ms)[0m  INSERT INTO "projects" ("repo_url", "user_id", "homepage_url", "name", "sites_https_status", "sites_https_justification", "repo_public_status", "repo_public_justification", "repo_track_status", "repo_track_justification", "repo_distributed_status", "repo_distributed_justification", "contribution_status", "contribution_justification", "discussion_status", "discussion_justification", "license_location_status", "license_location_justification", "release_notes_status", "release_notes_justification", "build_status", "build_justification", "build_common_tools_status", "build_common_tools_justification", "badge_percentage", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27) RETURNING "id"  [["repo_url", "https://github.com/ciitest/cii-best-practices-badge"], ["user_id", 764040364], ["homepage_url", "https://bestpractices.coreinfrastructure.org"], ["name", "Core Infrastructure Initiative Best Practices Badge"], ["sites_https_status", "Met"], ["sites_https_justification", "Given only https: URLs."], ["repo_public_status", "Met"], ["repo_public_justification", "Repository on GitHub, which provides public git repositories with URLs."], ["repo_track_status", "Met"], ["repo_track_justification", "Repository on GitHub, which uses git. git can track the changes, who made them, and when they were made."], ["repo_distributed_status", "Met"], ["repo_distributed_justification", "Repository on GitHub, which uses git. git is distributed."], ["contribution_status", "Met"], ["contribution_justification", "Non-trivial contribution file in repository: <https://github.com/ciitest/cii-best-practices-badge/blob/master/CONTRIBUTING.md>."], ["discussion_status", "Met"], ["discussion_justification", "GitHub supports discussions on issues and pull requests."], ["license_location_status", "Met"], ["license_location_justification", "Non-trivial license location file in repository: <https://github.com/ciitest/cii-best-practices-badge/blob/master/LICENSE>."], ["release_notes_status", "Met"], ["release_notes_justification", "Non-trivial release notes file in repository: <https://github.com/ciitest/cii-best-practices-badge/blob/master/CHANGELOG.md>."], ["build_status", "Met"], ["build_justification", "Non-trivial build file in repository: <https://github.com/ciitest/cii-best-practices-badge/blob/master/Rakefile>."], ["build_common_tools_status", "Met"], ["build_common_tools_justification", "Non-trivial build file in repository: <https://github.com/ciitest/cii-best-practices-badge/blob/master/Rakefile>."], ["badge_percentage", 15], ["created_at", "2016-05-31 02:02:03.602250"], ["updated_at", "2016-05-31 02:02:03.602250"]]
  [1m[36mSQL (0.4ms)[0m  [1mINSERT INTO "versions" ("event", "whodunnit", "created_at", "item_id", "item_type") VALUES ($1, $2, $3, $4, $5) RETURNING "id"[0m  [["event", "create"], ["whodunnit", "764040364"], ["created_at", "2016-05-31 02:02:03.602250"], ["item_id", 980190963], ["item_type", "Project"]]
  [1m[35m (0.5ms)[0m  COMMIT
Redirected to http://127.0.0.1:31337/projects/980190963/edit
Completed 302 Found in 117ms (ActiveRecord: 4.3ms)
Started GET "/projects/980190963/edit" for 127.0.0.1 at 2016-05-30 22:02:03 -0400
Processing by ProjectsController#edit as HTML
  Parameters: {"id"=>"980190963"}
  [1m[36mUser Load (0.2ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m  [["id", 764040364]]
  [1m[35mProject Load (0.7ms)[0m  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1  ORDER BY "projects"."created_at" ASC LIMIT 1  [["id", 980190963]]
  [1m[36mProject Exists (0.3ms)[0m  [1mSELECT  1 AS one FROM "projects" WHERE "projects"."user_id" = $1 AND "projects"."id" = $2 LIMIT 1[0m  [["user_id", 764040364], ["id", 980190963]]
  Rendered projects/_details.html.erb (0.0ms)
  Rendered shared/_error_messages.html.erb (0.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (3.3ms)
  Rendered projects/_status_chooser.html.erb (2.4ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (3.9ms)
  Rendered projects/_status_chooser.html.erb (3.3ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (3.2ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.4ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.7ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (2.8ms)
  Rendered projects/_status_chooser.html.erb (2.9ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.6ms)
  Rendered projects/_status_chooser.html.erb (2.7ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_status_chooser.html.erb (2.1ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.1ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_status_chooser.html.erb (1.9ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.4ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (3.1ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (4.6ms)
  Rendered projects/_status_chooser.html.erb (3.6ms)
  Rendered projects/_status_chooser.html.erb (3.1ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.6ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_status_chooser.html.erb (2.1ms)
  Rendered projects/_status_chooser.html.erb (3.6ms)
  Rendered projects/_status_chooser.html.erb (3.7ms)
  Rendered projects/_status_chooser.html.erb (3.6ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (3.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.7ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_status_chooser.html.erb (2.1ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (3.9ms)
  Rendered projects/_status_chooser.html.erb (2.7ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (4.0ms)
  Rendered projects/_status_chooser.html.erb (2.8ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.5ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_status_chooser.html.erb (2.0ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.1ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.6ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.4ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.2ms)
  Rendered projects/_details.html.erb (0.0ms)
  Rendered projects/_status_chooser.html.erb (2.3ms)
  Rendered projects/_form.html.erb (202.1ms)
  Rendered projects/edit.html.erb within layouts/application (202.6ms)
  Rendered layouts/_header.html.erb (0.7ms)
  Rendered layouts/_footer.html.erb (1.3ms)
Completed 200 OK in 214ms (Views: 207.2ms | ActiveRecord: 1.1ms)
Started GET "/users/764040364" for 127.0.0.1 at 2016-05-30 22:02:04 -0400
Processing by UsersController#show as HTML
  Parameters: {"id"=>"764040364"}
  [1m[35mUser Load (0.2ms)[0m  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 764040364]]
  [1m[36mUser Load (0.7ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m  [["id", 764040364]]
  [1m[35mProject Load (3.6ms)[0m  SELECT "projects".* FROM "projects" WHERE "projects"."repo_url" IN ('https://github.com/ciitest/cii-best-practices-badge', 'https://github.com/ciitest/test-repo')  ORDER BY "projects"."created_at" ASC
  [1m[36mProject Load (1.1ms)[0m  [1mSELECT  "projects".* FROM "projects" WHERE "projects"."user_id" = $1  ORDER BY "projects"."created_at" ASC LIMIT 30 OFFSET 0[0m  [["user_id", 764040364]]
  [1m[35mProject Exists (0.3ms)[0m  SELECT  1 AS one FROM "projects" WHERE "projects"."user_id" = $1 LIMIT 1  [["user_id", 764040364]]
  Rendered projects/_table.html.erb (2.3ms)
  Rendered users/show.html.erb within layouts/application (6.9ms)
  Rendered layouts/_header.html.erb (1.1ms)
  Rendered layouts/_footer.html.erb (1.1ms)
Completed 200 OK in 48ms (Views: 13.5ms | ActiveRecord: 5.9ms)
Started GET "/projects/980190963/badge" for 127.0.0.1 at 2016-05-30 22:02:04 -0400
Processing by ProjectsController#badge as SVG
  Parameters: {"id"=>"980190963"}
  [1m[36mUser Load (0.2ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1[0m  [["id", 764040364]]
  [1m[35mProject Load (0.8ms)[0m  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1  ORDER BY "projects"."created_at" ASC LIMIT 1  [["id", 980190963]]
  Rendered text template (0.0ms)
Sent data  (0.7ms)
Completed 200 OK in 8ms (Views: 0.6ms | ActiveRecord: 1.0ms)
  [1m[36m (0.3ms)[0m  [1mROLLBACK[0m
  [1m[35m (0.1ms)[0m  BEGIN
----------------------------------------------

Contrast this with a later test that sets SAVEPOINT and ROLLBACK normally:

--------------------------------------------------------
ProjectStatTest: test_project_stat_matches_expect_values
--------------------------------------------------------
  [1m[36m (0.0ms)[0m  [1mSAVEPOINT active_record_1[0m
  [1m[35m (0.0ms)[0m  SELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 0)
  [1m[36m (0.0ms)[0m  [1mSELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 25)[0m
  [1m[35m (0.0ms)[0m  SELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 50)
  [1m[36m (0.0ms)[0m  [1mSELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 75)[0m
  [1m[35m (0.0ms)[0m  SELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 90)
  [1m[36m (0.0ms)[0m  [1mSELECT COUNT(*) FROM "projects" WHERE ("projects"."badge_percentage" >= 100)[0m
  [1m[35m (0.0ms)[0m  SELECT COUNT(*) FROM "projects" WHERE ("projects"."created_at" >= '2015-02-28 12:00:00.000000')
  [1m[36m (0.0ms)[0m  [1mSELECT COUNT(*) FROM "projects" WHERE ("projects"."updated_at" >= '2015-02-28 12:00:00.000000')[0m
  [1m[35mSQL (0.0ms)[0m  INSERT INTO "project_stats" ("created_at", "updated_at", "percent_ge_0", "percent_ge_25", "percent_ge_50", "percent_ge_75", "percent_ge_90", "percent_ge_100", "created_since_yesterday", "updated_since_yesterday") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id"  [["created_at", "2015-03-01 12:00:00.000000"], ["updated_at", "2015-03-01 12:00:00.000000"], ["percent_ge_0", 5], ["percent_ge_25", 2], ["percent_ge_50", 2], ["percent_ge_75", 2], ["percent_ge_90", 1], ["percent_ge_100", 1], ["created_since_yesterday", 5], ["updated_since_yesterday", 0]]
  [1m[36m (0.0ms)[0m  [1mRELEASE SAVEPOINT active_record_1[0m
  [1m[35m (0.1ms)[0m  ROLLBACK
  [1m[36m (0.1ms)[0m  [1mBEGIN[0m
--------------------------------------------

Any ideas how to get this test to set a SAVEPOINT so that the ROLLBACK will succeed? I suspect I'm encounter a bug with VCR (which I filed at https://github.com/vcr/vcr/issues/586 ) but it's hard to be sure.

Dan Kohn
  • 33,811
  • 9
  • 84
  • 100

1 Answers1

0

From a quick glance it looks like you're using transactional testing with tests marked js: true - That doesn't work because when using drivers that support JS Capybara loads the app being tested in a separate thread. Each thread (the tests and the app) then have their own connection to the database and a transaction started in one thread isn't visible to the other thread until it's committed (which may never happen due to transactional testing). It also means records created in the app aren't cleaned up since they weren't actually created in the test transaction which gets rolled back. You probably want to look at something like DatabaseCleaner for use with your js: true tagged tests - a blend of https://github.com/DatabaseCleaner/database_cleaner#minitest-example and https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example is a good starting place

Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78