<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-05-09T09:31:42+00:00</updated><id>/feed.xml</id><title type="html">Kasper Heyndrickx</title><subtitle>Kasper's personal blog</subtitle><entry><title type="html">Contract testing with Prism</title><link href="/2024/05/04/contract-testing-with-prism.html" rel="alternate" type="text/html" title="Contract testing with Prism" /><published>2024-05-04T10:42:34+00:00</published><updated>2024-05-04T10:42:34+00:00</updated><id>/2024/05/04/contract-testing-with-prism</id><content type="html" xml:base="/2024/05/04/contract-testing-with-prism.html"><![CDATA[<h2 id="the-difficulties-of-testing-with-external-systems">The difficulties of testing with external systems</h2>

<p>All systems interact with external components at some point. This can be a database, another microservice from your own team, a microservice from another team, or even a service outside of your company. Most interactions with databases can be tested by spinning up a <a href="https://java.testcontainers.org/modules/databases/">database testcontainer</a>. Other microservices maintained by your own team are also relatively easy. You’re well aware of how they’re deployed, how they behave and what dependencies they have. In most cases you can deploy these in a generic testcontainer. This leaves us with external services, these are usually the most challenging. There are a few common approaches here:</p>

<ul>
  <li>Deploying the external service in a provided testcontainer</li>
  <li>Deploying the external service in a generic testcontainer</li>
  <li>Mocking the service</li>
  <li>Testing with a live sandbox instance</li>
  <li>Not testing the interaction directly</li>
</ul>

<p>The first option is preferred, but not always available. The next best option would be to deploy the service in a generic testcontainer. This is assuming you have access to the docker image of this service, which isn’t always the case. Also, the service might depend on other services too. Trying to deploy all this can quickly become too complicated.</p>

<p>The most common approach is to mock the external service. But there’s one major problem with this: the behavior of your mock might not match the actual behavior of the external service. Also, if you decide to use a newer api version, it’s highly unlikely that you check if the behavior of your mocks still matches reality. And finally, it’s just a lot of work. Manually writing the expected output for each interaction becomes tiring real quick.</p>

<p>So what’s the alternative?</p>

<h2 id="stoplight-prism">Stoplight Prism</h2>

<p><a href="https://github.com/stoplightio/prism">Stoplight Prism</a> is a tool to generate a mock API based on an <a href="https://spec.openapis.org/oas/latest.html">OpenAPI specification</a>. I prefer to also run this in container, so that my test can spin up the prism service by itself. Let’s take a look how this would work in a simple python example:</p>

<p><em><code class="language-plaintext highlighter-rouge">post_example.py</code></em></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>

<span class="k">def</span> <span class="nf">add_item</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">item</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="n">path</span> <span class="o">=</span> <span class="s">"/api/items"</span>
    <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s">"Content-Type"</span><span class="p">:</span> <span class="s">"application/json"</span><span class="p">}</span>
    <span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"name"</span><span class="p">:</span> <span class="n">item</span><span class="p">}</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">url</span><span class="si">}{</span><span class="n">path</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> 
                             <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> 
                             <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
</code></pre></div></div>

<p>The method we want to test (<code class="language-plaintext highlighter-rouge">add_item(url, item)</code>) makes a POST request to an external service. We want to test that our interaction with this external service is correct. Is the URL correct? Is the body correct? Do we correctly make the request? We could do this by writing a mock, but as mentioned before, our mocks might be wrong. Let’s see how we can use the provided openAPI specification to create a mock service:</p>

<p><em><code class="language-plaintext highlighter-rouge">resources/api.yaml</code></em></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">openapi</span><span class="pi">:</span> <span class="s">3.0.0</span>
<span class="na">info</span><span class="pi">:</span>
  <span class="na">title</span><span class="pi">:</span> <span class="s">Items API</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">paths</span><span class="pi">:</span>
  <span class="na">/api/items</span><span class="pi">:</span>
    <span class="na">post</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s">Add a new item</span>
      <span class="na">requestBody</span><span class="pi">:</span>
        <span class="na">required</span><span class="pi">:</span> <span class="no">true</span>
        <span class="na">content</span><span class="pi">:</span>
          <span class="na">application/json</span><span class="pi">:</span>
            <span class="na">schema</span><span class="pi">:</span>
              <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
              <span class="na">properties</span><span class="pi">:</span>
                <span class="na">name</span><span class="pi">:</span>
                  <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">responses</span><span class="pi">:</span>
        <span class="s1">'</span><span class="s">200'</span><span class="err">:</span>
          <span class="na">description</span><span class="pi">:</span> <span class="s">Successful operation</span>
</code></pre></div></div>

<p><em><code class="language-plaintext highlighter-rouge">test_post_example.py</code></em></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pytest</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">testcontainers.core.container</span> <span class="kn">import</span> <span class="n">DockerContainer</span>
<span class="kn">from</span> <span class="nn">testcontainers.core.waiting_utils</span> <span class="kn">import</span> <span class="n">wait_for_logs</span>


<span class="k">class</span> <span class="nc">TestService</span><span class="p">:</span>
    <span class="o">@</span><span class="n">pytest</span><span class="p">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s">"module"</span><span class="p">,</span> <span class="n">autouse</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">service_container</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="n">prism</span> <span class="o">=</span> <span class="n">DockerContainer</span><span class="p">(</span><span class="s">"stoplight/prism:4"</span><span class="p">,</span> <span class="n">init</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">prism</span><span class="p">.</span><span class="n">with_volume_mapping</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">getcwd</span><span class="p">()</span> <span class="o">+</span> <span class="s">'/resources'</span><span class="p">,</span> <span class="s">'/apis'</span><span class="p">)</span> \
            <span class="p">.</span><span class="n">with_exposed_ports</span><span class="p">(</span><span class="mi">4010</span><span class="p">)</span> \
            <span class="p">.</span><span class="n">with_command</span><span class="p">(</span><span class="s">"mock -v 'debug' -h 0.0.0.0 /apis/api.yaml"</span><span class="p">)</span> \
            <span class="p">.</span><span class="n">start</span><span class="p">()</span>
        <span class="n">wait_for_logs</span><span class="p">(</span><span class="n">prism</span><span class="p">,</span> <span class="s">"Prism is listening on http://0.0.0.0:4010"</span><span class="p">)</span>

        <span class="k">def</span> <span class="nf">remove_container</span><span class="p">():</span>
            <span class="n">prism</span><span class="p">.</span><span class="n">stop</span><span class="p">()</span>

        <span class="n">request</span><span class="p">.</span><span class="n">addfinalizer</span><span class="p">(</span><span class="n">remove_container</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">prism</span>
        
    <span class="k">def</span> <span class="nf">test_add_item</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">service_container</span><span class="p">):</span>
        <span class="n">url</span> <span class="o">=</span> <span class="s">"http://localhost:"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">service_container</span><span class="p">.</span><span class="n">get_exposed_port</span><span class="p">(</span><span class="mi">4010</span><span class="p">))</span>
        <span class="k">assert</span> <span class="n">add_item</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="s">'test'</span><span class="p">)</span> <span class="o">==</span> <span class="bp">True</span>
</code></pre></div></div>

<p>The test itself is defined in <code class="language-plaintext highlighter-rouge">test_add_item(self, service_container)</code>. Before this method is called, a Prism container spins up. The OpenAPI yaml configuration is passed to the container by attaching it in a volume. We wait until the container is ready (by waiting for logs), and then continue with the actual test.</p>

<p>Now when we call <code class="language-plaintext highlighter-rouge">add_item()</code> in the test, it will send the request to the Prism container, which validates the validity of the request. If our request doesn’t match the API specification, we’ll know about it!</p>

<h2 id="specifying-the-response">Specifying the response</h2>

<p>The way our test is set up now gives us no control over the response of Prism. This might be enough in some cases, but sooner or later you’ll want to test some logic that depends on the values returned by the external service. Let’s take a look at what that might look like in practise. Let’s consider an API that returns books based on their title. The response includes a ‘status’ field that can be “IN STOCK” or “SOLD OUT”. We use this API in a function that returns ‘True’ if the book is in stock, and ‘False’ if the book is Sold out.</p>

<p>Here’s what our method looks like:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>

<span class="k">def</span> <span class="nf">in_stock</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">book_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">headers</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span> <span class="o">+</span> <span class="s">'/api/books'</span><span class="p">,</span> 
                            <span class="n">params</span><span class="o">=</span><span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="n">book_name</span><span class="p">},</span> 
                            <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">data</span><span class="p">[</span><span class="s">'status'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'IN STOCK'</span>
</code></pre></div></div>

<p>We cannot make any assumptions as to what our Prism mock server will return. Therefore we cannot guarantee that this method will return True or False when we test it. We need a way to tell Prism what to respond to a certain request. To make this work, we need to add predefined examples to the OpenAPI specification. Prism allows us to define which example to return depending on a value passed through the header. Notice that out function definition now allows us to pass headers to the request.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">openapi</span><span class="pi">:</span> <span class="s">3.0.0</span>
<span class="na">info</span><span class="pi">:</span>
  <span class="na">title</span><span class="pi">:</span> <span class="s">Bookstore API</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">paths</span><span class="pi">:</span>
  <span class="na">/api/books</span><span class="pi">:</span>
    <span class="na">get</span><span class="pi">:</span>
      <span class="na">parameters</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">name</span>
          <span class="na">in</span><span class="pi">:</span> <span class="s">query</span>
          <span class="na">required</span><span class="pi">:</span> <span class="no">true</span>
          <span class="na">schema</span><span class="pi">:</span>
            <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">responses</span><span class="pi">:</span>
        <span class="s1">'</span><span class="s">200'</span><span class="err">:</span>
          <span class="na">description</span><span class="pi">:</span> <span class="s">OK</span>
          <span class="na">content</span><span class="pi">:</span>
            <span class="na">application/json</span><span class="pi">:</span>
              <span class="na">schema</span><span class="pi">:</span>
                <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
                <span class="na">properties</span><span class="pi">:</span>
                  <span class="na">name</span><span class="pi">:</span>
                    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
                  <span class="na">status</span><span class="pi">:</span>
                    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
                    <span class="na">enum</span><span class="pi">:</span>
                      <span class="pi">-</span> <span class="s">IN STOCK</span>
                      <span class="pi">-</span> <span class="s">SOLD OUT</span>
              <span class="na">examples</span><span class="pi">:</span>
                <span class="na">inStockExample</span><span class="pi">:</span>
                  <span class="na">value</span><span class="pi">:</span>
                    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">The</span><span class="nv"> </span><span class="s">Great</span><span class="nv"> </span><span class="s">Gatsby"</span>
                    <span class="na">status</span><span class="pi">:</span> <span class="s2">"</span><span class="s">IN</span><span class="nv"> </span><span class="s">STOCK"</span>
                  <span class="na">summary</span><span class="pi">:</span> <span class="s">Example of a book in stock</span>
                <span class="na">soldOutExample</span><span class="pi">:</span>
                  <span class="na">value</span><span class="pi">:</span>
                    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1984"</span>
                    <span class="na">status</span><span class="pi">:</span> <span class="s2">"</span><span class="s">SOLD</span><span class="nv"> </span><span class="s">OUT"</span>
                  <span class="na">summary</span><span class="pi">:</span> <span class="s">Example of a sold out book</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">TestService</span><span class="p">:</span>
    <span class="o">@</span><span class="n">pytest</span><span class="p">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s">"module"</span><span class="p">,</span> <span class="n">autouse</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">service_container</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="c1"># omitted, see previous example
</span>        
    <span class="k">def</span> <span class="nf">test_in_stock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">service_container</span><span class="p">):</span>
        <span class="n">url</span> <span class="o">=</span> <span class="s">"http://localhost:"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">service_container</span><span class="p">.</span><span class="n">get_exposed_port</span><span class="p">(</span><span class="mi">4010</span><span class="p">))</span>
        <span class="k">assert</span> <span class="bp">True</span> <span class="o">==</span> <span class="n">in_stock</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="s">'The Great Gatsby'</span><span class="p">,</span> 
                                <span class="p">{</span><span class="s">"prefer"</span><span class="p">:</span> <span class="s">"example=inStockExample"</span><span class="p">})</span>

    <span class="k">def</span> <span class="nf">test_sold_out</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">service_container</span><span class="p">):</span>
        <span class="n">url</span> <span class="o">=</span> <span class="s">"http://localhost:"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">service_container</span><span class="p">.</span><span class="n">get_exposed_port</span><span class="p">(</span><span class="mi">4010</span><span class="p">))</span>
        <span class="k">assert</span> <span class="bp">False</span> <span class="o">==</span> <span class="n">in_stock</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="s">'1984'</span><span class="p">,</span> 
                                 <span class="p">{</span><span class="s">"prefer"</span><span class="p">:</span> <span class="s">"example=soldOutExample"</span><span class="p">})</span>
</code></pre></div></div>

<p>The downside here is that we need to modify our production code so that we can inject headers in the test. This is not always desired, and sometimes it’s not even possible. The OpenAPI specification also needs to include examples that we can use in our tests. In most cases you’ll have to add these manually. So if the external service updates their API, you cannot just swap the specification and be done with it.</p>

<h2 id="injecting-headers-and-adding-examples">Injecting headers and adding examples</h2>

<p>What if we don’t want or can’t inject headers into our HTTP request? Stoplight has an example of how to implement a proxy server to work around this issue in <a href="https://github.com/stoplightio/ExampleChooserPrismProxy">this repository</a>. It’s a service that sits between your test and the Prism mock server. This proxy looks at your request and adds the right ‘prefer’ header, based on predefined logic. It then forwards this query to Prism, and returns the result as-is.</p>

<p>Adding the examples to the OpenAPI spec seems a bit more problematic. It’s not ideal to modify the specification directly, because then we cannot easily update it. At the time of writing, I haven’t found a nice solution to add examples to OpenAPI specifications. It’s not that difficult to write your own, but it comes with the added problem that Prism doesn’t validate examples to the spec.</p>

<p>It’s perfectly possible to create an example that doesn’t match the defined response format. Currently I use Prism alongside traditional mocks. I use Prism to verify that my interaction with the API matches the API specification, and I use mocks to test my logic based on different potential responses.</p>]]></content><author><name></name></author><category term="testing" /><category term="prism" /><category term="python" /><summary type="html"><![CDATA[The difficulties of testing with external systems]]></summary></entry><entry><title type="html">Configuring multiple git accounts</title><link href="/2024/05/03/multiple-git-accounts.html" rel="alternate" type="text/html" title="Configuring multiple git accounts" /><published>2024-05-03T10:20:21+00:00</published><updated>2024-05-03T10:20:21+00:00</updated><id>/2024/05/03/multiple-git-accounts</id><content type="html" xml:base="/2024/05/03/multiple-git-accounts.html"><![CDATA[<h2 id="git-configuration">Git configuration</h2>

<p>Git is a set it and forget it setup for most people. Every time I get my hands on a new machine, I always have to go through the same guides. It’s not difficult, but since I only do this every year or two, it’s not something I tend to remember. Recently I configured git with multiple accounts (private and work), which is a bit more challenging than just blindly following a guide on how to set up an ssh key.</p>

<p>In this post I’ll try to explain step by step how to set up git with two accounts. How to commit with different usernames, how to use the right ssh key depending on which repository you’re pushing to, and how to sign your commits with different GPG keys.</p>

<p>In this example I assume two github accounts: one private and one work account. On the private repository, I want to make commits with my private email address. On The work repository, I want to make commits with my company email address.</p>

<h2 id="git-users">Git users</h2>

<p>I assume you’ve already set up git with one account. When you run <code class="language-plaintext highlighter-rouge">git config user.name &amp;&amp; git config user.email</code>, it should show your git username and email address. This user data is added to all your commits.</p>

<p>One way to use a different configuration on different repositories is by setting <code class="language-plaintext highlighter-rouge">git config --local user.name "My Name"</code>. However, the annoying thing is that you have to remember to do this for every new repository. Luckily git introduced <a href="https://git-scm.com/docs/git-config#_includes">conditional configuration includes</a> in v2.13. In my case, I clone private repositories to <code class="language-plaintext highlighter-rouge">~/Documents/private/repos</code>, while work repositories go to <code class="language-plaintext highlighter-rouge">~/Documents/work/repos</code>. To use a different user name and email based on the repository location, I modify <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> as follows:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">includeIf</span> <span class="s2">"gitdir:~/Documents/private/repos"</span>]
  <span class="n">path</span> = .<span class="n">gitconfig</span>-<span class="n">private</span>
[<span class="n">includeIf</span> <span class="s2">"gitdir:~/Documents/work/repos"</span>]
  <span class="n">path</span> = .<span class="n">gitconfig</span>-<span class="n">work</span>
</code></pre></div></div>

<p>To set up the actual username and email, I need to create two new files: <code class="language-plaintext highlighter-rouge">~/.gitconfig-work</code> and <code class="language-plaintext highlighter-rouge">~/.gitconfig-private</code>. These look like normal git config files. For example, gitconfig-work might look like this:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">user</span>]
        <span class="n">name</span> = <span class="n">my</span> <span class="n">name</span>
        <span class="n">email</span> = <span class="n">name</span>@<span class="n">company</span>.<span class="n">com</span>
</code></pre></div></div>

<p>To test if this works, <code class="language-plaintext highlighter-rouge">cd</code> to any repository and run <code class="language-plaintext highlighter-rouge">git config user.email</code>. It should return your private email address on private repositories, and your work email for work repositories.</p>

<h2 id="ssh-keys">SSH keys</h2>

<p>Now that we can <code class="language-plaintext highlighter-rouge">git commit</code> under different usernames, let’s see how we can <code class="language-plaintext highlighter-rouge">git push</code> using different credentials. To do this we first need to create a new ssh key and add it to our github account. Make sure to give the new key a different name when prompted. I name both keys <code class="language-plaintext highlighter-rouge">~/.ssh/id_ed25519_gh_private</code> and <code class="language-plaintext highlighter-rouge">~/.ssh/id_ed25519_gh_work</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen <span class="nt">-t</span> ed25519 <span class="nt">-C</span> <span class="s2">"name@company.com"</span>
</code></pre></div></div>

<p>Then navigate to your account settings on github and click on <a href="https://github.com/settings/keys">‘SSH and GPG keys’</a>. Copy the public key (the file ending in <code class="language-plaintext highlighter-rouge">.pub</code> in the <code class="language-plaintext highlighter-rouge">~/.ssh/</code> directory) and add it as a new SSH Key on github. Make sure to log in to the right github account first.</p>

<p>The only thing left to do now is configure git to use the right ssh key on the right repository. Remember the two <code class="language-plaintext highlighter-rouge">.gitconfig</code> files we created earlier? We’ll add an extra section to both files to configure the ssh command. My <code class="language-plaintext highlighter-rouge">~/.gitconfig-work</code> file now looks like this:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">user</span>]
        <span class="n">name</span> = <span class="n">my</span> <span class="n">name</span>
        <span class="n">email</span> = <span class="n">name</span>@<span class="n">company</span>.<span class="n">com</span>
[<span class="n">core</span>]
        <span class="n">sshCommand</span> = <span class="n">ssh</span> -<span class="n">i</span> ~/.<span class="n">ssh</span>/<span class="n">id_ed25519_gh_work</span>
</code></pre></div></div>

<p>If everything is set up correctly, you should now be able to push and pull from both private and work repositories.</p>

<h2 id="gpg">GPG</h2>

<p>The process of initially setting up a GPG key can be confusing, but configuring multiple keys is relatively easy. There’s not really any configuration required. If your commit is made with a certain name and email address, then the right key will automatically be used to sign the commit.</p>

<p>As a quick reminder, setting up a new gpg key with github can be done as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># First generate the key</span>
gpg <span class="nt">--full-generate-key</span>

<span class="c"># find the right key id</span>
gpg <span class="nt">--list-secret-keys</span> <span class="nt">--keyid-format</span><span class="o">=</span>long

<span class="c"># Find the right id in the output after the algorithm:</span>
<span class="c"># sec    rsa4096/546E539071567BD2 2024-03-06 [SC] [expires: 2028-03-06]</span>
<span class="c"># the key here is '546E539071567BD2'</span>

<span class="c"># export the public key</span>
gpg <span class="nt">--armor</span> <span class="nt">--export</span> 546E539071567BD2
</code></pre></div></div>

<p>From this output, copy the part beginning with <code class="language-plaintext highlighter-rouge">-----BEGIN PGP PUBLIC KEY BLOCK-----</code> and ending with <code class="language-plaintext highlighter-rouge">-----END PGP PUBLIC KEY BLOCK-----</code>. This key should then be added under ‘GPG keys’ on github.</p>

<p>If you want to add GPG signing to only one account and not for the ohter, then this can also be done by modifying the matching <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> files. For example:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">commit</span>]
        <span class="n">gpgsign</span> = <span class="n">true</span> / <span class="n">false</span>
</code></pre></div></div>

<p>That’s it! Now git is fully set up to work with multiple accounts.</p>]]></content><author><name></name></author><category term="git" /><category term="github" /><summary type="html"><![CDATA[Git configuration]]></summary></entry><entry><title type="html">Always lint python typing</title><link href="/2024/05/02/python-typing-without-linter.html" rel="alternate" type="text/html" title="Always lint python typing" /><published>2024-05-02T19:19:37+00:00</published><updated>2024-05-02T19:19:37+00:00</updated><id>/2024/05/02/python-typing-without-linter</id><content type="html" xml:base="/2024/05/02/python-typing-without-linter.html"><![CDATA[<h2 id="dynamically-and-statically-typed-languages">Dynamically and statically typed languages</h2>

<p>Unlike statically typed languages like Java, C++ and others, Python is a dynamically typed language. This means that the type of the variable isn’t fixed. To illustrate this, let’s look at some valid Python code in the following example.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def addOne(number) :
  return number + 1

print(addOne(5)) # prints '6'
print(addOne("5")) # TypeError: can only concatenate str (not "int") to str
</code></pre></div></div>

<p>So while it does throw an error when the code is executed, it won’t prevent you from packaging and deploying your code. However, the same thing is not possible in a statically typed language like java:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int addOne(int num) {
    return num + 1;
}

void main() {
  System.out.println(addOne(1));
  System.out.println(addOne("1"));
}
// error: error: incompatible types: String cannot be converted to int
</code></pre></div></div>

<p>This is a compile error, so even if we’d want to, we cannot run this code. The compiler doesn’t let us. In many cases, this is an advantage. It’s impossible to accidentally pass a number to a function that expects a string. Also, the function definitions and how to use them tend to be more clear. Especially when codebases grow larger, or when dealing with external libraries, it isn’t always obvious which types are used where if it isn’t explicitly defined.</p>

<p>So how does python deal with this?</p>

<h2 id="python-typing">Python typing</h2>

<p>By default, python does not require you to define the type when declaring a variable. However, Python 3 introduced type hinting in the <a href="https://docs.python.org/3/library/typing.html">typing module</a>. While this is great, it’s actually nothing more than adding documentation to your code according to a predefined format. Let’s look at the previous example again, but this time using type hints:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def addOne(number : int) -&gt; int:
  return number + 1

print(addOne(5)) # prints '6'
print(addOne("5")) # TypeError: can only concatenate str (not "int") to str
</code></pre></div></div>

<p>The types of the variables are defined here, as well as the type of the return value. However, it didn’t prevent anything. In fact, you can claim that the function takes in a String and returns a list. The code will still run, and the error will still be the same.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def addOne(number : str) -&gt; list:
  return number + 1 
</code></pre></div></div>
<p>All these type hints are nothing but documentation, that’s kept up to date by nothing more than good intentions. Anyone who has worked on large codebases know that documentation quickly becomes outdated, unless there’s a strong culture of doing code reviews, or some kind of an automated check is in place, to match the documentation to the actual code.</p>

<h2 id="linting">Linting</h2>

<p>The only way to get true benefit from type hints, and to keep them up to date, is to introduce a linter that checks for them. A linter is a tool that performs static analysis on your code. In other words, it checks for errors without actually running the code. The first python linter that does type checking is <a href="https://mypy-lang.org/">mypy</a>. Let’s take a look what it outputs for our previous example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mypy example.py
example.py:5: error: Argument 1 to "addOne" has incompatible type "str"; expected "int"  [arg-type]
</code></pre></div></div>

<p>Exactly what we need! We can now add this linter to our CI/CD pipeline, and stop typing issues before they’re even deployed. In conclusion, type hints are nice, but without a linter to enforce their correctness, a lot of their value is lost.</p>]]></content><author><name></name></author><category term="python" /><category term="testing" /><summary type="html"><![CDATA[Dynamically and statically typed languages]]></summary></entry><entry><title type="html">Don’t overwrite docker tags</title><link href="/2022/11/26/dont-overwrite-docker-tags.html" rel="alternate" type="text/html" title="Don’t overwrite docker tags" /><published>2022-11-26T13:51:22+00:00</published><updated>2022-11-26T13:51:22+00:00</updated><id>/2022/11/26/dont-overwrite-docker-tags</id><content type="html" xml:base="/2022/11/26/dont-overwrite-docker-tags.html"><![CDATA[<h1 id="the-ambiguity-of-docker-tags">The ambiguity of docker tags</h1>
<p>You might have already heard that using the <a href="https://vsupalov.com/docker-latest-tag/">:latest tag in docker is bad practise</a>. This is true for several reasons. For starters, a new image might be pushed without adding the latest tag. Meaning that if you pull image:latest, it might not actually be the latest image.</p>

<p>On top of that, if you’re running a new container, you might not really be using the latest image, even if you’re using the latest tag. Not all tools will automatically check if a new version is available. If you already have an image tagged as <code class="language-plaintext highlighter-rouge">latest</code> locally, docker will not automatically pull a new image.</p>

<p>I’d like to extend this reasoning for other tags. Let’s say you already pulled <code class="language-plaintext highlighter-rouge">image:0.0.1</code>, but someone else pushed a new image using the same tag. Next time you execute <code class="language-plaintext highlighter-rouge">docker run image:0.0.1</code>, it will not automatically pull the updated image. You’ll still be using the ‘old’ version 0.0.1.</p>

<p>This problem can be avoided by using <code class="language-plaintext highlighter-rouge">docker run --pull=always</code>, but what about containers that are already running? If you’re not careful, you will have two different versions of version 0.0.1 running. If that sentence sounds confusing, that’s because it is.</p>

<p>Don’t overwrite docker tags.</p>

<h1 id="automated-madness">Automated madness</h1>

<p>I wouldn’t be writing this if I hadn’t recently run into this problem. Our container registry allowed to overwrite docker images, and our continuous integration pipelines (CI/CD) did not automatically bump versions. This was a project with very little changes once it was set up, and any change usually was accompanied with a redeployment of most of our infrastructure.</p>

<p>This was fine for the first couple of months, but then Dependabot was introduced on here, as well as on some other projects. It started occasionally opening pull requests, which in turn automatically triggered a run on our CI/CD.</p>

<p>I already talked about bad practise: allowing overwriting docker images, but that in itself would not have caused problems. There was a bug in our CI/CD, that also published docker images (under the same tag) when running checks on Pull Requests (PR).  Since there were so few changes, and all changes were usually immediately accepted, this remained unnoticed for a long time.</p>

<p>Dependabot automatically updated some dependencies, this triggered an automated build of the project, which was automatically published on an existing tag.</p>

<p>The final piece of the puzzle was Kubernetes, where our application was running. Kubernetes will not automatically redeploy a new image when it’s available, but it will always pull the image when starting a new container. Several days after the PR from Dependabot was opened, Kubernetes had to restart a failed pod, and pulled the new image.</p>

<p>At this point we had two different instances of the same application running side by side, but looking at the pod definition, they were identical. This whole process did not involve any human interaction.</p>

<h1 id="mitigations">Mitigations</h1>

<p>While this could have been avoided by not publishing builds when running CI/CD on pull requests, the real issue was pushing images with tags that already existed.</p>

<p>Unfortunately Azure Container Registry (ACR), where our images are hosted, does not allow automatically locking image tags once in use, but <a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-image-lock#lock-an-image-by-tag">you can lock existing tags individually</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az acr repository update \
    --name myregistry --repository myrepo \
    --write-enabled false
</code></pre></div></div>

<p>It can be a good idea to run this as part of your CI/CD each time an image is published. 
With some registries, such as <a href="https://jfrog.com/artifactory/">JFrog Artifactory</a> you can <a href="https://www.jfrog.com/confluence/display/RTF6X/Managing+Permissions">prevent overwriting existing deployments</a>.</p>]]></content><author><name></name></author><category term="docker" /><category term="devops" /><summary type="html"><![CDATA[The ambiguity of docker tags You might have already heard that using the :latest tag in docker is bad practise. This is true for several reasons. For starters, a new image might be pushed without adding the latest tag. Meaning that if you pull image:latest, it might not actually be the latest image.]]></summary></entry><entry><title type="html">Keep your DTO’s dumb</title><link href="/2022/11/26/dumb-dtos.html" rel="alternate" type="text/html" title="Keep your DTO’s dumb" /><published>2022-11-26T11:35:37+00:00</published><updated>2022-11-26T11:35:37+00:00</updated><id>/2022/11/26/dumb-dtos</id><content type="html" xml:base="/2022/11/26/dumb-dtos.html"><![CDATA[<p>Data Transfer Objects should do nothing more than their name implies. Apart from transferring data, they should do nothing. They should be completely unaware of where their data comes from, where it’s going, or why it’s going there. Sounds pretty obvious, doesn’t it? So why am I repeating this?</p>

<p>As with all things in life, nothing is as easy as it appears. Let me walk you through a bug I dealt with a while ago, and share why it could ultimately be blamed on ‘smart’ DTO’s.</p>

<p>I had just started working on a new project. As usual, I first made sure it can be built and tested locally. Almost immediately it became obvious that even the unit tests in this project behaved rather unpredictably. Some tests would sometimes fail and sometimes pass, even without changing anything. Surprisingly not everyone on the team was suffering from this problem. Tests would also never fail when they ran in isolation. There was only an issue when I ran the entire testsuite.</p>

<p>But who ever runs the whole testsuite locally? Everyone just pushes their code and let the CI/CD pipeline do the rest. Surprisingly though, when running the whole testsuite on Jenkins, it behaved as expected. So the problem remained largely unnoticed.</p>

<p>All good and well, but what does this have to do with DTO’s?
I’ll get to that, don’t worry. It will quickly make sense once we take a closer look at what one of the unstable tests looked like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Test
void createsNewObject() throws Exception {
  final MockHttpServletRequestBuilder request = 
    RestDocumentationRequestBuilders.put("/endpoint")
      .content(asJsonString(MY_DTO_OBJECT))
      .contentType(APPLICATION_JSON);
  when(processor.process(MY_DTO_OBJECT)).thenReturn(NEW_OBJECT_RESPONSE);

  final ResultActions result = mockMvc.perform(request);

  result.andExpect(status().is(Matchers.in(new Integer[]{200, 201})));
}
</code></pre></div></div>

<p>This test failed with a NullPointerException, somewhere in the controller we’re testing. More specifically, processor.process() returned null. Which is weird, since the argument is in fact MY_DTO_OBJECT, and we explicitly state that in this case, it should return NEW_OBJECT_RESPONSE.</p>

<p>The attentive reader probably already figured out that this cannot possibly be correct. First of all, because the test wouldn’t fail if it was, and secondly, because we never directly pass MY_DTO_OBJECT to the controller. It’s passed as a JSON object and rebuilt by Spring. Therefore, the argument cannot be equal.</p>

<p>But that shouldn’t matter. Mockito argument matching calls Object.equals(Object other); And we do in fact Override the equals method on our DTO. Let’s take a closer look at what kind of DTO we’re dealing with. It’s heavily simplified though, just to illustrate the point.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public class MyDto {
    private final URL theURL;

    public URL getTheURL() {
        return this.theURL;
    }

    public boolean equals(final Object o) {
        if (o == this) return true;
        if (!(o instanceof MyDto)) return false;
        return Objects.equals(this.getTheURL(), ((MyDto)o).getTheURL());
    }

    public int hashCode() { /* skipped */ }
}
</code></pre></div></div>

<p>A URL looks pretty innocent, but a quick Google search shows that URL.equals() is completely broken. In fact, this has been known since 2006. Mystery solved! Our argument matching did not work since the URLs in our DTO need to be equal, and that can behave unpredictably because it depends on your DNS settings.</p>

<p>Most discussions on this problem will tell you to use URI instead, but I’d go a step further and say that for DTO’s, even URI is overkill. The Data Transfer Object should not know how to use this data, it should only transfer the data itself. Let the end-user deal with creating an URI from a given String, and handle all the exceptions that go with it.</p>

<p>For DTO’s, POJO‘s suffice.</p>]]></content><author><name></name></author><category term="java" /><category term="testing" /><summary type="html"><![CDATA[Data Transfer Objects should do nothing more than their name implies. Apart from transferring data, they should do nothing. They should be completely unaware of where their data comes from, where it’s going, or why it’s going there. Sounds pretty obvious, doesn’t it? So why am I repeating this?]]></summary></entry></feed>