require "test_helper" class DocumentsApiTest < ActionDispatch::IntegrationTest setup do @anna = create_user("anna@example.com") @bob = create_user("bob@example.com") @doc = create_document(@anna) end def put_content(doc, base_version, headers: {}) patch "/api/documents/#{doc.id}", params: { content: DOC_JSON, base_version: }, headers:, as: :json end test "listing and creating need an account" do get "/api/documents" assert_response :unauthorized post "/api/documents", params: { title: "x", content: DOC_JSON }, as: :json assert_response :unauthorized end test "create (upload), list, read" do sign_in @anna post "/api/documents", params: { title: "Hochgeladen", content: DOC_JSON }, as: :json assert_response :created id = json.dig("document", "id") get "/api/documents" assert_equal [ id, @doc.id ].sort, json["documents"].map { _1["id"] }.sort get "/api/documents/#{id}" assert_equal DOC_JSON, json.dig("document", "content") assert_equal "owner", json.dig("document", "role") end test "someone else's document is a 404" do sign_in @bob get "/api/documents/#{@doc.id}" assert_response :not_found end test "save with version check; stale base_version is a conflict" do sign_in @anna put_content(@doc, 1) assert_response :success assert_equal 2, json.dig("document", "version") put_content(@doc, 1) assert_response :conflict assert_equal 2, json["version"] end test "known_version returns metadata only when unchanged" do sign_in @anna get "/api/documents/#{@doc.id}", params: { known_version: 1 } assert json["unchanged"] assert_nil json.dig("document", "content") end test "edit lock blocks saves from another tab" do sign_in @anna post "/api/documents/#{@doc.id}/lock", headers: { "X-Client-Id" => "tab-a" } assert json.dig("lock", "mine") post "/api/documents/#{@doc.id}/lock", headers: { "X-Client-Id" => "tab-b" } assert_not json.dig("lock", "mine") put_content(@doc, 1, headers: { "X-Client-Id" => "tab-b" }) assert_response :locked delete "/api/documents/#{@doc.id}/lock", headers: { "X-Client-Id" => "tab-a" } put_content(@doc, 1, headers: { "X-Client-Id" => "tab-b" }) assert_response :success end test "share link without account: read with token; viewer cannot write" do link = @doc.share_links.create!(role: :viewer, created_by: @anna) headers = { "X-Share-Token" => link.token } post "/api/documents/#{@doc.id}/join", params: { token: link.token }, as: :json assert_equal "viewer", json.dig("document", "role") get "/api/documents/#{@doc.id}", headers: headers assert_response :success put_content(@doc, 1, headers:) assert_response :forbidden get "/api/documents/#{@doc.id}", headers: { "X-Share-Token" => "wrong" } assert_response :not_found end test "share link with account: joining adds it to the list" do link = @doc.share_links.create!(role: :editor, created_by: @anna) sign_in @bob post "/api/documents/#{@doc.id}/join", params: { token: link.token }, as: :json get "/api/documents" assert_equal [ [ @doc.id, "editor" ] ], json["documents"].map { [ _1["id"], _1["role"] ] } put_content(@doc, 1) assert_response :success end test "owner deletes; a member only leaves" do DocumentMembership.grant!(document: @doc, user: @bob, role: :editor, source: :invite) sign_in @bob delete "/api/documents/#{@doc.id}" assert_response :no_content assert Document.exists?(@doc.id) assert_nil @doc.role_for(user: @bob) sign_in @anna delete "/api/documents/#{@doc.id}" assert_not Document.exists?(@doc.id) end end