Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / axios
Every number on this page was produced by running both implementations over the same cases: the real axios package pinned at 1.7.9 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See axios for the port's own documentation. Source: github.com/malcolmston/axios.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| bodies | 25 | 25 | 0 | 100.0% |
| headers | 23 | 23 | 0 | 100.0% |
| uri | 23 | 23 | 0 | 100.0% |
| params | 22 | 22 | 0 | 100.0% |
| status | 18 | 18 | 0 | 100.0% |
| pipeline | 16 | 16 | 0 | 100.0% |
| verbs | 13 | 13 | 0 | 100.0% |
| network | 11 | 11 | 0 | 100.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: cd parity/axios/node && node inventory.js # 140 symbols. A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| axios.request | Client.Request | differs | verb-method-uppercase, verb-default-method, verb-absolute-url-overrides-baseurl, verb-baseurl-with-path, body-get-with-data | a baseURL that carries a path is dropped by the port |
| axios.getUri | Client.GetUri | differs | geturi-* | bracket encoding, default array format, baseURL path, param order |
| axios.delete | Client.Delete | match | verb-delete, verb-delete-with-body | neither side accepts a body argument |
| axios.get | Client.Get | match | verb-get | |
| axios.head | Client.Head | match | verb-head | |
| axios.options | Client.Options | match | verb-options | |
| axios.post | Client.Post | match | verb-post-json | |
| axios.postForm | — | missing | — | no multipart convenience wrapper |
| axios.put | Client.Put | match | verb-put-json | |
| axios.putForm | — | missing | — | |
| axios.patch | Client.Patch | match | verb-patch-json | |
| axios.patchForm | — | missing | — | |
| axios.defaults | Default(), SetDefault() | untested | — | the port exposes a default *Client, not a mutable defaults object |
| axios.interceptors | Config.RequestInterceptors, Config.ResponseInterceptors | differs | interceptor-* | a rejecting interceptor is wrapped in an AxiosError by the port; axios propagates it verbatim |
| axios.create | Create(), New() | match | every request case | |
| axios.Axios | Client | match | every request case | |
| axios.CanceledError | CanceledError | untested | — | no cancellation case (cancel timing is not deterministic across runners) |
| axios.CancelToken | CancelToken, NewCancelToken | untested | — | |
| axios.isCancel | IsCancel | match | every error case (isCancel field) | |
| axios.VERSION | — | missing | — | the repo has a VERSION file but no exported constant |
| axios.toFormData | NewFormData(), FormData | differs | body-multipart | the port omits the trailing CRLF after the closing boundary |
| axios.AxiosError | Error | differs | status-4xx/5xx, error-*, timeout-* | different code values (see divergences) |
| axios.Cancel | — | missing | — | no Cancel alias for CanceledError |
| axios.all | All | match | verb-all-parallel | |
| axios.spread | Spread | untested | — | the Go signature only accepts func(...*Response) T, so no shared case is expressible |
| axios.isAxiosError | IsAxiosError | differs | interceptor-request-rejects, transform-request-throws, error-invalid-url | the port reports true for errors axios leaves as plain Error/TypeError |
| axios.mergeConfig | MergeConfig | match | mergeconfig-* | |
| axios.AxiosHeaders | — | missing | — | the port uses net/http.Header directly |
| axios.formToJSON | FormToJSON | match | formtojson-* | |
| axios.getAdapter | — | missing | — | no pluggable adapter registry |
| axios.HttpStatusCode | — | missing | — | no status-code enum |
| Axios#request | Client.Request | differs | see axios.request | |
| Axios#getUri | Client.GetUri | differs | see axios.getUri | |
| Axios#delete | Client.Delete | match | verb-delete | |
| Axios#get | Client.Get | match | verb-get | |
| Axios#head | Client.Head | match | verb-head | |
| Axios#options | Client.Options | match | verb-options | |
| Axios#post | Client.Post | match | verb-post-json | |
| Axios#postForm | — | missing | — | |
| Axios#put | Client.Put | match | verb-put-json | |
| Axios#putForm | — | missing | — | |
| Axios#patch | Client.Patch | match | verb-patch-json | |
| Axios#patchForm | — | missing | — | |
| AxiosHeaders#set | http.Header.Set (stdlib) | missing | — | no port-owned headers type; stdlib covers the operation |
| AxiosHeaders#get | http.Header.Get / Response.Header | missing | — | stdlib equivalent |
| AxiosHeaders#has | — | missing | — | |
| AxiosHeaders#delete | http.Header.Del (stdlib) | missing | — | stdlib equivalent |
| AxiosHeaders#clear | — | missing | — | |
| AxiosHeaders#normalize | — | missing | — | http.Header canonicalises on Set/Get instead |
| AxiosHeaders#concat | — | missing | — | |
| AxiosHeaders#toJSON | — | missing | — | |
| AxiosHeaders#getContentType | Response.ContentType | missing | — | response-side only; no request-header accessor |
| AxiosHeaders#setContentType | RequestConfig.ContentType | missing | — | a config field, not a headers method |
| AxiosHeaders#hasContentType | — | missing | — | |
| AxiosHeaders#getContentLength | Response.ContentLength | missing | — | response-side only |
| AxiosHeaders#setContentLength | — | missing | — | |
| AxiosHeaders#hasContentLength | — | missing | — | |
| AxiosHeaders#getAccept | — | missing | — | |
| AxiosHeaders#setAccept | — | missing | — | |
| AxiosHeaders#hasAccept | — | missing | — | |
| AxiosHeaders#getAcceptEncoding | — | missing | — | |
| AxiosHeaders#setAcceptEncoding | — | missing | — | |
| AxiosHeaders#hasAcceptEncoding | — | missing | — | |
| AxiosHeaders#getUserAgent | — | missing | — | |
| AxiosHeaders#setUserAgent | — | missing | — | |
| AxiosHeaders#hasUserAgent | — | missing | — | |
| AxiosHeaders#getAuthorization | — | missing | — | |
| AxiosHeaders#setAuthorization | Config.BasicAuth, Config.BearerToken | missing | headers-basic-auth | expressed as config, not a headers method |
| AxiosHeaders#hasAuthorization | — | missing | — | |
| AxiosError.from | — | missing | — | no error-wrapping constructor |
| AxiosError#toJSON | Error.ToJSON, Error.MarshalJSON | untested | — | field sets differ; no shared case |
| AxiosError#isAxiosError | IsAxiosError | differs | see axios.isAxiosError | |
| CancelToken#throwIfRequested | — | missing | — | |
| CancelToken#subscribe | — | missing | — | the port exposes CancelToken.Context() instead |
| CancelToken#unsubscribe | — | missing | — | |
| CancelToken#toAbortSignal | AbortSignal, AbortController | missing | — | no conversion from CancelToken |
| defaults.transitional | — | missing | — | |
| defaults.adapter | — | missing | — | |
| defaults.transformRequest | Config.TransformRequest | differs | transform-request-* | the port transforms the *encoded* bytes and still sets Content-Type; axios replaces the serializer |
| defaults.transformResponse | Config.TransformResponse | match | transform-response-* | |
| defaults.timeout | Config.Timeout | differs | timeout-* | error code differs (ECONNABORTED vs ERR_NETWORK) |
| defaults.xsrfCookieName | Config.XSRFCookieName | untested | — | the port needs a cookie jar; not exercised |
| defaults.xsrfHeaderName | Config.XSRFHeaderName | untested | — | |
| defaults.maxContentLength | Config.MaxContentLength | differs | response-max-content-length-* | error code differs |
| defaults.maxBodyLength | Config.MaxBodyLength | differs | body-max-body-length-* | see config.data — Content-Type divergence dominates |
| defaults.env | — | missing | — | |
| defaults.validateStatus | Config.ValidateStatus | differs | status-* | error code differs for 4xx and for non-2xx rejected by a custom predicate |
| defaults.headers | Config.Headers | differs | headers-* | null does not remove a header in the port; no default Accept |
| defaults.headers.common | HeaderDefaults.Common | match | headers-group-common, headers-group-and-flat-together | |
| defaults.headers.delete | HeaderDefaults.Delete | match | headers-group-delete | |
| defaults.headers.get | HeaderDefaults.Get | match | headers-group-get | |
| defaults.headers.head | HeaderDefaults.Head | match | headers-group-head | |
| defaults.headers.post | HeaderDefaults.Post | match | headers-group-method-matching, headers-group-method-not-matching | |
| defaults.headers.put | HeaderDefaults.Put | match | headers-group-put | |
| defaults.headers.patch | HeaderDefaults.Patch | untested | — | field exists, no case |
| transitional.silentJSONParsing | Response.Text fallback | match | response-invalid-json-body | both yield the raw string for malformed application/json |
| transitional.forcedJSONParsing | — | differs | response-autoparse-json-without-content-type | axios JSON-parses regardless of Content-Type; the port never auto-parses |
| transitional.clarifyTimeoutError | — | missing | — | |
| config.url | Client.Request rawURL | match | verb-* | |
| config.method | Client.Request method | match | verb-method-uppercase, verb-default-method |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| body-json-object | bodies | defaults.transformRequest (object -> JSON) | EncodeBody (map -> JSON) | match | |
| body-json-nested | bodies | defaults.transformRequest | EncodeBody | match | |
| body-json-array | bodies | defaults.transformRequest | EncodeBody | match | |
| body-json-null | bodies | defaults.transformRequest (null body) | EncodeBody(nil) | match | |
| body-plain-string | bodies | defaults.transformRequest (string) | EncodeBody(string) | match | axios sends a raw string with no Content-Type; the port picks text/plain |
| body-string-with-explicit-content-type | bodies | config.headers['Content-Type'] | RequestConfig.Headers | match | |
| body-urlencoded | bodies | URLSearchParams body | EncodeBody(url.Values) | match | |
| body-bytes | bodies | Buffer body | EncodeBody([]byte) | match | |
| body-multipart | bodies | FormData body | FormData | match | boundary token is normalised to BOUNDARY on both sides |
| body-empty-string | bodies | defaults.transformRequest ('') | EncodeBody("") | match | |
| body-get-with-data | bodies | axios#request (GET + data) | Client.Request | match | a body on GET |
| body-unicode-json | bodies | defaults.transformRequest | EncodeBody | match | Go's encoding/json escapes <, > and & by default |
| response-parsed-json | bodies | defaults.transformResponse | Response.JSON | match | |
| response-text-endpoint | bodies | res.data (text/plain) | Response.Text | match | |
| response-autoparse-json-without-content-type | bodies | transitional.forcedJSONParsing | Response.Text (no auto parsing) | match | axios JSON-parses regardless of Content-Type; the port never auto-parses |
| response-invalid-json-body | bodies | transitional.silentJSONParsing | Response.Text | match | application/json with a malformed body: axios silently falls back to the string |
| response-type-text | bodies | config.responseType = 'text' | ResponseText | match | |
| response-type-json | bodies | config.responseType = 'json' | ResponseJSON | match | |
| response-status-text | bodies | res.statusText | Response.StatusText | match | axios reports 'OK'; the port reports '200 OK' |
| response-204-no-content | bodies | res.data on 204 | Response.Text on 204 | match | |
| response-max-content-length-exceeded | bodies | config.maxContentLength | Config.MaxContentLength | match | |
| response-max-content-length-ok | bodies | config.maxContentLength | Config.MaxContentLength | match | |
| body-max-body-length-exceeded | bodies | config.maxBodyLength | Config.MaxBodyLength | match | |
| body-max-body-length-ok | bodies | config.maxBodyLength | Config.MaxBodyLength | match | |
| response-decompress-false | bodies | config.decompress = false | Config.Decompress | match | asserts the Accept-Encoding actually sent when decompression is disabled |
| headers-default-accept | headers | defaults.headers.common.Accept | DefaultHeaderGroups | match | axios sends 'application/json, text/plain, */*' by default |
| headers-default-accept-encoding | headers | http adapter Accept-Encoding | net/http Accept-Encoding | match | axios asks for gzip, compress, deflate, br |
| headers-default-user-agent | headers | defaults.headers['User-Agent'] | net/http User-Agent | deviation | the port advertises its own version (axios/0.3.0), not the pinned upstream axios/1.7.9; it will never string-match a specific upstream release. See API-DEVIATIONS.md. |
| headers-request-custom | headers | config.headers | RequestConfig.Headers | match | |
| headers-client-default | headers | defaults.headers | Config.Headers | match | |
| headers-merge-client-and-request | headers | mergeConfig headers | Config.Headers + RequestConfig.Headers | match | |
| headers-request-overrides-client | headers | mergeConfig headers | Config.Headers + RequestConfig.Headers | match | |
| headers-case-insensitive-override | headers | AxiosHeaders normalisation | http.Header canonicalisation | match | differently-cased keys must collapse to one header |
| headers-group-common | headers | defaults.headers.common | HeaderDefaults.Common | match | |
| headers-group-method-matching | headers | defaults.headers.post | HeaderDefaults.Post | match | |
| headers-group-method-not-matching | headers | defaults.headers.post | HeaderDefaults.Post | match | a post-scoped default must not appear on GET |
| headers-group-get | headers | defaults.headers.get | HeaderDefaults.Get | match | |
| headers-group-and-flat-together | headers | defaults.headers.common + per-request | HeaderDefaults + Config.Headers | match | |
| headers-basic-auth | headers | config.auth | Config.BasicAuth | match | asserts the exact Authorization value on the wire |
| headers-basic-auth-per-request | headers | config.auth | RequestConfig.BasicAuth | match | |
| headers-basic-auth-unicode-password | headers | config.auth (non-latin1) | Config.BasicAuth | match | axios throws for non-latin1 credentials |
| headers-explicit-authorization-vs-auth | headers | config.auth overriding headers.Authorization | BasicAuth vs Headers | match | |
| headers-numeric-value | headers | config.headers (number value) | RequestConfig.Headers | match | |
| headers-response-exposed | headers | res.headers | Response.Headers | match | response header keys must be lower-cased and complete |
| headers-group-put | headers | defaults.headers.put | HeaderDefaults.Put | match | |
| headers-group-delete | headers | defaults.headers.delete | HeaderDefaults.Delete | match | |
| headers-group-head | headers | defaults.headers.head | HeaderDefaults.Head | match | |
| headers-null-value-removes | headers | config.headers value null | Headers with nil value | match | axios treats a null header value as a removal |
| timeout-exceeded | network | config.timeout | Config.Timeout | match | axios reports code ECONNABORTED |
| timeout-not-exceeded | network | config.timeout | Config.Timeout | match | |
| timeout-per-request-overrides-client | network | mergeConfig timeout | RequestConfig.Timeout | match | |
| timeout-zero-means-none | network | config.timeout = 0 | Config.Timeout = 0 | match | |
| redirect-single-followed | network | follow-redirects default | net/http default redirect policy | match | final observed path must be /echo |
| redirect-chain-of-three | network | follow-redirects default | net/http default redirect policy | match | |
| redirect-max-redirects-zero | network | config.maxRedirects = 0 | Config.MaxRedirects = 0 | match | axios: do not follow, return the 302 (which then fails validateStatus); the port treats 0 as 'use the default 10' |
| redirect-max-redirects-zero-validate-all | network | config.maxRedirects = 0 + validateStatus all | Config.MaxRedirects = 0 | match | |
| redirect-max-redirects-negative | network | config.maxRedirects = -1 | Config.MaxRedirects < 0 disables following | match | |
| redirect-exceeds-max | network | ERR_FR_TOO_MANY_REDIRECTS | redirect cap error | match | |
| redirect-within-max | network | config.maxRedirects | Config.MaxRedirects | match | |
| params-single-scalar | params | config.params | RequestConfig.ParamsMap | match | |
| params-space-and-symbols | params | config.params | RequestConfig.ParamsMap | match | percent-encoding of space, & and = |
| params-unicode | params | config.params | RequestConfig.ParamsMap | match | |
| params-empty-string | params | config.params | RequestConfig.ParamsMap | match | |
| params-null-dropped | params | config.params | RequestConfig.ParamsMap | match | axios drops null/undefined params entirely |
| params-boolean | params | config.params | RequestConfig.ParamsMap | match | |
| params-number | params | config.params | RequestConfig.ParamsMap | match | |
| params-array-default-format | params | config.params (array, default serializer) | ArrayFormatRepeat (zero value) | match | axios default is a[]=1&a[]=2 |
| params-array-indexes-null | params | paramsSerializer.indexes = null | ArrayFormatRepeat | match | |
| params-array-indexes-false | params | paramsSerializer.indexes = false | ArrayFormatBrackets | match | |
| params-array-indexes-true | params | paramsSerializer.indexes = true | ArrayFormatIndices | match | |
| params-nested-object | params | config.params (nested object) | FlattenParams | match | |
| params-deeply-nested-object | params | config.params (nested object) | FlattenParams | match | |
| params-array-of-objects | params | config.params (array of objects) | FlattenParams | match | |
| params-multi-key-order | params | config.params (insertion order) | SerializeParams | deviation | the port's params are Go maps (map[string]any / url.Values), which have no insertion order; keys are emitted sorted for determinism. See API-DEVIATIONS.md. |
| params-merge-client-and-request | params | mergeConfig params | Config.ParamsMap + RequestConfig.ParamsMap | match | axios mergeConfig: request params replace client params wholesale |
| params-client-only | params | defaults.params | Config.ParamsMap | match | |
| params-existing-query-in-url | params | config.params + url query | BuildURL | match | axios appends with & after the existing query string |
| params-custom-serializer | params | paramsSerializer.serialize | RequestConfig.ParamsSerializer | match | a custom serializer must fully replace the built-in encoding |
| params-client-serializer-inherited | params | defaults.paramsSerializer | Config.ParamsSerializer | match | |
| params-empty-object | params | config.params = {} | RequestConfig.ParamsMap | match | no query string should be appended |
| params-empty-array | params | config.params (empty array) | FlattenParams | match | |
| interceptor-request-adds-header | pipeline | interceptors.request.use | Config.RequestInterceptors | match | |
| interceptor-request-two-in-order | pipeline | interceptors.request.use x2 | Config.RequestInterceptors | match | axios runs request interceptors in reverse registration order; both headers must still land |
| interceptor-request-rejects | pipeline | interceptors.request.use (throwing) | RequestInterceptor returning error | match | the request must never be sent; both sides must fail |
| interceptor-response-tags-header | pipeline | interceptors.response.use | Config.ResponseInterceptors | match | |
| interceptor-response-rejects | pipeline | interceptors.response.use (throwing) | ResponseInterceptor returning error | match | |
| interceptor-response-not-run-on-status-error | pipeline | interceptors.response fulfilled handler on 500 | ResponseInterceptors on 500 | match | a 500 must not reach the fulfilled response interceptor |
| transform-request-prefix | pipeline | config.transformRequest | Config.TransformRequest | match | axios transforms replace the default JSON serializer, so no Content-Type is set; the port transforms after encoding |
| transform-request-prefix-string-body | pipeline | config.transformRequest | Config.TransformRequest | match | |
| transform-request-can-set-header | pipeline | config.transformRequest (headers arg) | RequestTransform header mutation | match | |
| transform-request-throws | pipeline | config.transformRequest (throwing) | RequestTransform returning error | match | |
| transform-request-client-level | pipeline | defaults.transformRequest | Config.TransformRequest | match | |
| transform-response-identity-keeps-raw-text | pipeline | config.transformResponse | Config.TransformResponse | match | identity transform replaces axios' JSON parsing, leaving a string |
| transform-response-upper | pipeline | config.transformResponse | Config.TransformResponse | match | |
| transform-response-wrap | pipeline | config.transformResponse | Config.TransformResponse | match | |
| transform-response-client-level | pipeline | defaults.transformResponse | Config.TransformResponse | match | |
| transform-response-runs-on-error-status | pipeline | transformResponse on 500 | TransformResponse on 500 | match | |
| status-200-default-validate | status | defaults.validateStatus | default 2xx predicate | match | |
| status-201-default-validate | status | defaults.validateStatus | default 2xx predicate | match | |
| status-299-default-validate | status | defaults.validateStatus | default 2xx predicate | match |