Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
9123687
feat: initial resumable upload implementation
viacheslav-rostovtsev Aug 17, 2026
b44af71
fix: missing fillbuffer instruction
viacheslav-rostovtsev Sep 2, 2026
cdb42c9
fix: clarified recovery on missing headers
viacheslav-rostovtsev Sep 2, 2026
eb2f38f
fixdoc: errors description in the table
viacheslav-rostovtsev Sep 2, 2026
04f0ccb
fix: nailed down Cat 2 classification
viacheslav-rostovtsev Sep 2, 2026
72a2846
fixed: separated start retry policy from control plane policy
viacheslav-rostovtsev Sep 2, 2026
3546633
invalid events error and correct return type
viacheslav-rostovtsev Sep 2, 2026
ff5a4d7
tests: rules classification
viacheslav-rostovtsev Sep 2, 2026
12be42c
tests: chunk size negotiation
viacheslav-rostovtsev Sep 2, 2026
9b6d844
tests: buffer realign
viacheslav-rostovtsev Sep 3, 2026
8462aba
test: error mapping in driver
viacheslav-rostovtsev Sep 3, 2026
f2f0ab7
tests: retry policies
viacheslav-rostovtsev Sep 3, 2026
1cc1810
fixed the callback documentation and added callback tests
viacheslav-rostovtsev Sep 3, 2026
8da1801
feat: sensible defaults for the global deadline, driver tests update
viacheslav-rostovtsev Sep 5, 2026
7319e80
feat: reclassify timeouts
viacheslav-rostovtsev Sep 5, 2026
1eb61ef
fix: minor fixes
viacheslav-rostovtsev Sep 5, 2026
9764ccf
remove user_override for start policy param from config
viacheslav-rostovtsev Sep 5, 2026
cea9e9e
feat: integration harness
viacheslav-rostovtsev Sep 5, 2026
a294dfb
test: first integration test success
viacheslav-rostovtsev Sep 6, 2026
59a5255
fix: adjust showcase test skip placement
viacheslav-rostovtsev Sep 6, 2026
bc308a1
feat: dedicated struct for progress notification
viacheslav-rostovtsev Sep 6, 2026
b652d9e
fix: missing slash in path
viacheslav-rostovtsev Sep 6, 2026
f386738
refactor: progress construction
viacheslav-rostovtsev Sep 6, 2026
454f861
refactor: decisions
viacheslav-rostovtsev Sep 6, 2026
b50d7ac
fix: update decision, refactor tests
viacheslav-rostovtsev Sep 6, 2026
9c40f65
feat: proper logging in Driver
viacheslav-rostovtsev Sep 6, 2026
5e8d962
refactor: logging in driver
viacheslav-rostovtsev Sep 6, 2026
8fbccfc
test: corpus-level tests
viacheslav-rostovtsev Sep 6, 2026
7b94672
fix: nailing down the recipes in a fixed list
viacheslav-rostovtsev Sep 6, 2026
40d8605
fix: reshuffle logging
viacheslav-rostovtsev Sep 6, 2026
29b1486
fix: log client id
viacheslav-rostovtsev Sep 6, 2026
402c6c1
feat: client stub changes to avoid logging large requests
viacheslav-rostovtsev Sep 7, 2026
0816ce2
test: added logging on failing tests in integration
viacheslav-rostovtsev Sep 7, 2026
b92334b
docs: update docs for logging
viacheslav-rostovtsev Sep 7, 2026
ed2ad7f
docs: sync docs
viacheslav-rostovtsev Sep 7, 2026
9318e67
tests: case 2 of golden path, separate doc for integration testing
viacheslav-rostovtsev Sep 7, 2026
70f1622
test: case 3 of golden path
viacheslav-rostovtsev Sep 7, 2026
7864a6b
feat: add phase to progress notification
viacheslav-rostovtsev Sep 7, 2026
8ddadfc
chore: a bit of docs
viacheslav-rostovtsev Sep 7, 2026
970a428
fix: bound transport retries
viacheslav-rostovtsev Sep 7, 2026
bd386cf
fix: boundary condition around transport timeout
viacheslav-rostovtsev Sep 7, 2026
a34cfbf
fix: configurable retry policies
viacheslav-rostovtsev Sep 7, 2026
07ce995
tests: nail down some invariants
viacheslav-rostovtsev Sep 7, 2026
ec86495
tests: integration testing helper defaults
viacheslav-rostovtsev Sep 8, 2026
903b9cb
tests: recovery integration
viacheslav-rostovtsev Sep 8, 2026
051b4a0
test: small fixes
viacheslav-rostovtsev Sep 8, 2026
23eaad8
feat: actionable errors for rest-resumable
viacheslav-rostovtsev Sep 9, 2026
e9d6b88
feat: log response body for actionable errors
viacheslav-rostovtsev Sep 9, 2026
7bbcbe3
fix: errors differentiation
viacheslav-rostovtsev Sep 9, 2026
3356656
fix: simplify error construction, add test'
viacheslav-rostovtsev Sep 10, 2026
8ec859e
docs: sync
viacheslav-rostovtsev Sep 10, 2026
ba08baf
test: integration suite A
viacheslav-rostovtsev Sep 10, 2026
41c011e
fix: test adjustment
viacheslav-rostovtsev Sep 10, 2026
6296b5c
chore: better documentation
viacheslav-rostovtsev Sep 10, 2026
f8b475c
feat: add resume handle
viacheslav-rostovtsev Sep 10, 2026
b1b1992
feat: adding ResumeUploadCOnfig
viacheslav-rostovtsev Sep 10, 2026
549485e
fix: RequestFailedError
viacheslav-rostovtsev Sep 10, 2026
8e89fef
guard against over-forwarding the stream
viacheslav-rostovtsev Sep 10, 2026
2854ee6
upload session
viacheslav-rostovtsev Sep 10, 2026
cd7ae72
test: fix minor
viacheslav-rostovtsev Sep 10, 2026
65f327b
chore: revert stream offset, only allow one run per session
viacheslav-rostovtsev Sep 10, 2026
47debcc
integration helpers
viacheslav-rostovtsev Sep 10, 2026
2048e7e
test: integration tests for resume
viacheslav-rostovtsev Sep 10, 2026
9ff0918
test: small fixes, reference-implementation synced
viacheslav-rostovtsev Sep 10, 2026
6edfd6b
chore: update documentation
viacheslav-rostovtsev Sep 14, 2026
5a64361
docs: improve state machine documentation, reify event shapes
viacheslav-rostovtsev Sep 14, 2026
b6eef11
chore: refactor the configuration structures and clarify documentatio…
viacheslav-rostovtsev Sep 14, 2026
c7985ab
fix: improved header filtering
viacheslav-rostovtsev Sep 14, 2026
116562b
fix: better testing for headers change
viacheslav-rostovtsev Sep 15, 2026
7d9129f
docs: correct YARD type for the response body
viacheslav-rostovtsev Sep 15, 2026
9c6b288
fix: remove duplicate cancel event, enforce invariants
viacheslav-rostovtsev Sep 15, 2026
be3d344
doc: update documentation on new exception
viacheslav-rostovtsev Sep 15, 2026
68603d7
docs: retry policies
viacheslav-rostovtsev Sep 15, 2026
04271c6
fix: minor cleanup
viacheslav-rostovtsev Sep 15, 2026
a7e0fb8
fix: minor doc fixes and test ajustment around header verification
viacheslav-rostovtsev Sep 15, 2026
b0336f3
docs: remove stale, move actual, adjust links
viacheslav-rostovtsev Sep 15, 2026
076ab0b
chore:undo year change
viacheslav-rostovtsev Sep 15, 2026
c098487
chore: mark a constant private
viacheslav-rostovtsev Sep 15, 2026
e8a5a65
fix: toys
viacheslav-rostovtsev Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gapic-common/.toys.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
t.fail_on_undocumented_objects = false # TODO: Fix so this can be enabled
t.bundler = true
end
alias_tool :yard, :yardoc
tool :yard, delegate_relative: :yardoc

expand :gem_build

Expand Down
139 changes: 139 additions & 0 deletions gapic-common/.toys/test-integration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# frozen_string_literal: true

# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "socket"
require "tmpdir"

expand :minitest, name: "" do |t|
t.libs = ["lib", "integration"]
t.files = ["integration/**/*_test.rb"]
t.bundler = true
end

alias_method :run_minitest, :run

def run
if !ENV["SHOWCASE_ENDPOINT"].to_s.empty?
run_minitest
return
end

bin = resolve_showcase_bin
if bin.nil?
if !ENV["CI"].to_s.empty?
logger.error "No SHOWCASE_ENDPOINT or gapic-showcase binary found in CI environment."
exit 1
else
logger.warn "Skipping integration tests: no SHOWCASE_ENDPOINT or gapic-showcase binary found."
return
end
end

verify_showcase_version! bin

port = allocate_port
fallback_port = allocate_port
log_path = File.join Dir.tmpdir, "gapic-showcase-#{Process.pid}-#{Time.now.to_i}.log"

pid = Process.spawn(
bin, "run",
"--port", ":#{port}",
"--fallback-port", ":#{fallback_port}",
out: log_path,
err: log_path,
pgroup: true
)

begin
wait_for_showcase! pid, port, log_path
ENV["SHOWCASE_ENDPOINT"] = "http://localhost:#{port}"
run_minitest
ensure
if pid
begin
Process.kill "-TERM", pid
Process.waitpid pid
rescue Errno::ESRCH, Errno::ECHILD
# Process already terminated
end
end
end
end

def resolve_showcase_bin
env_bin = ENV["SHOWCASE_BIN"].to_s
return env_bin unless env_bin.empty?

ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).each do |dir|
candidate = File.join dir, "gapic-showcase"
return candidate if File.executable?(candidate) && !File.directory?(candidate)
end
nil
end

def verify_showcase_version!(bin)
output = begin
IO.popen([bin, "--version"], err: [:child, :out], &:read).strip
rescue StandardError => e
logger.error "Failed to execute '#{bin} --version': #{e.message}"
exit 1
end

version_match = output[/\d+\.\d+(?:\.\d+)*/]
if version_match.nil?
logger.error "Could not parse version from '#{bin} --version' output: #{output.inspect}"
exit 1
end

if Gem::Version.new(version_match) < Gem::Version.new("0.43")
logger.error "gapic-showcase version #{version_match} is too old (minimum required is 0.43)."
exit 1
end
end

def allocate_port
server = TCPServer.open "127.0.0.1", 0
port = server.addr[1]
server.close
port
end

def wait_for_showcase!(pid, port, log_path)
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 10.0

loop do
exited_pid, status = Process.waitpid2 pid, Process::WNOHANG
if exited_pid
logger.error "gapic-showcase exited prematurely (status: #{status.exitstatus}). Log file: #{log_path}"
exit 1
end

begin
sock = TCPSocket.new "127.0.0.1", port
sock.close
return
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
# Server not ready yet
end

if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
logger.error "Timed out waiting 10s for gapic-showcase to listen on port #{port}. Log file: #{log_path}"
exit 1
end

sleep 0.1
end
end
687 changes: 687 additions & 0 deletions gapic-common/design/resumable_upload/implementation-guide.md

Large diffs are not rendered by default.

286 changes: 286 additions & 0 deletions gapic-common/design/resumable_upload/integration-test-plan.md

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions gapic-common/integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Integration Tests

This directory contains integration tests for `gapic-common`, executed against a running `gapic-showcase` server via the `toys test-integration` command.

## Running Integration Tests

```bash
toys test-integration
```

You can pass standard Minitest flags to filter or seed test runs:

```bash
toys test-integration --name /resumable_upload/ --seed 1234
```

## Showcase Server Management & Lifecycle

The `toys test-integration` command (`.toys/test-integration.rb`) manages the `gapic-showcase` server lifecycle automatically:

1. **Existing Endpoint (`SHOWCASE_ENDPOINT`)**:
- If `ENV["SHOWCASE_ENDPOINT"]` is present and non-empty, `toys test-integration` skips binary resolution and runs the Minitest suite directly against that endpoint.

2. **Binary Resolution (`SHOWCASE_BIN` / `PATH`)**:
- When `SHOWCASE_ENDPOINT` is not set, the runner checks `ENV["SHOWCASE_BIN"]` first, then searches `ENV["PATH"]` for an executable `gapic-showcase` binary.
- **Version Check**: The runner executes `<binary> --version` and verifies that the version is at least `0.43`. If the version is older than `0.43`, it logs an error and exits with status `1`.

3. **Missing Endpoint and Binary**:
- **CI Environment (`ENV["CI"]` set)**: Fails immediately with exit status `1`.
- **Local Environment (`ENV["CI"]` unset)**: Logs an informational message and skips integration tests cleanly (exit status `0`).

4. **Ephemeral Port Allocation & Polling**:
- Allocates two ephemeral TCP ports on `127.0.0.1` for `--port :<port>` and `--fallback-port :<fallback_port>` to avoid port collisions across concurrent runs.
- Spawns `gapic-showcase run --port :<port> --fallback-port :<fallback_port>` in a dedicated process group (`pgroup: true`) with `stdout` and `stderr` redirected to a temporary log file in `Dir.tmpdir`.
- Polls `127.0.0.1:<port>` with a 10-second monotonic clock budget while checking `Process.waitpid2` (`WNOHANG`) on each iteration. If the process exits prematurely or fails to accept TCP connections within 10 seconds, the runner prints the path to the log file and exits with status `1`.

5. **Execution & Teardown**:
- Sets `ENV["SHOWCASE_ENDPOINT"] = "http://localhost:#{port}"` and runs the Minitest suite (`integration/**/*_test.rb`).
- An `ensure` block sends `SIGTERM` to the entire process group (`-TERM`) and reaps the child process so no background showcase processes are leaked.

## Logging and Diagnostics

Each integration test captures `DEBUG`-level client and driver logs into an in-memory buffer during execution:

- **Automatic Failure Dump**: If a test fails or raises an unhandled exception, the captured trace is automatically dumped to `stderr` during `teardown`.
- **Force Log Dump (`SHOWCASE_LOG`)**: Set `SHOWCASE_LOG=1` (or any non-empty value) to dump the captured trace for all executed tests, including passing ones:

```bash
SHOWCASE_LOG=1 toys test-integration
```

Loading
Loading