<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mormolis.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mormolis.github.io/" rel="alternate" type="text/html" /><updated>2026-08-15T19:57:46+00:00</updated><id>https://mormolis.github.io/feed.xml</id><title type="html">Yorgos Lamprakis’ digital home</title><subtitle>A (nearly) no-CSS, fast, minimalist Jekyll theme.
</subtitle><author><name>Georgios Lamprakis</name></author><entry><title type="html">Zuckerbergs Ai Manifesto</title><link href="https://mormolis.github.io/zuckerbergs-ai-manifesto.html" rel="alternate" type="text/html" title="Zuckerbergs Ai Manifesto" /><published>2026-08-15T00:00:00+00:00</published><updated>2026-08-15T00:00:00+00:00</updated><id>https://mormolis.github.io/Zuckerbergs-ai-manifesto</id><content type="html" xml:base="https://mormolis.github.io/zuckerbergs-ai-manifesto.html"><![CDATA[<h1 id="the-bullshit-in-zuckerbergs-manifestos">The bullshit in Zuckerberg’s Manifestos</h1>

<p>Zuckerberg came up with another manifesto! This time about foundation models and how humanity can benefit from AI. The Future is for Everyone: The path to a positive AI future. But it’s not his first one.</p>

<p>This article is co-authored with Claude opus 5.0.</p>

<p>The content is product of guided research and collaboration with it and it aims to expose why Zucky’s greediness should be criminalised. After having a look at his “social media manifesto” and a few relevant incidents we will take a look at the new one.</p>

<p>On 16 February 2017, Mark Zuckerberg published a roughly 5,800-word essay called “Building Global Community.” It promised that Facebook would build “social infrastructure” for the world. It promised communities that keep people safe, inform them, and support civic engagement. It promised the company would fight misinformation and protect its users. Lol (or tol - thinking of laughing, a term coined by the <a href="https://www.youtube.com/watch?v=Meg\_ejAXe3g">winekone</a>)</p>

<hr />

<h2 id="1-cambridge-analytica"><strong>1. Cambridge Analytica</strong></h2>

<p><strong>The promise:</strong> Facebook would protect its users and earn their trust.</p>

<p><strong>What happened:</strong> Roughly one year after the manifesto, Facebook confirmed that personal data from tens of millions of accounts had reached Cambridge Analytica, a political consultancy hired by the Trump campaign. Facebook had known about the leak since 2015. It did not tell the public. It asked the parties to delete the data and moved on. Zuckerberg later said the company did not deserve to serve users if it could not protect their data. The company paid a $5 billion privacy settlement to the US government.</p>

<p>Source: <a href="https://www.cnbc.com/2018/04/10/facebook-cambridge-analytica-a-timeline-of-the-data-hijacking-scandal.html">CNBC timeline of the Cambridge Analytica scandal</a></p>

<p>Also relevant: internal deposition testimony suggests Zuckerberg was weighing Cambridge Analytica as an election-security concern in 2017, the same year the manifesto was published, but a reference to the firm was removed from a public report on his advisers’ suggestion.</p>

<p>Source: <a href="https://www.cnn.com/2022/12/20/tech/zuckerberg-cambridge-analytica/index.html">CNN on Zuckerberg’s 2019 SEC deposition</a></p>

<hr />

<h2 id="2-onavo-and-project-ghostbusters"><strong>2. Onavo and Project Ghostbusters</strong></h2>

<p>This is the closest case to the manifesto in time, and Zuckerberg started it himself.</p>

<p><strong>The promise:</strong> Facebook would be a trustworthy custodian of user data, and a bottom-up layer that spreads power rather than hoards it.</p>

<p><strong>What happened:</strong> Facebook bought Onavo, an Israeli VPN app, in 2013. It marketed Onavo to users as a privacy tool. It was the opposite.</p>

<p>On 9 June 2016, Zuckerberg emailed executives complaining that Snapchat’s encrypted traffic left Facebook with no analytics on a fast-growing rival. He told them to figure out a new way to get reliable data, and to work out how.</p>

<p>The Onavo team came back a month later with a plan. They would install kits on iOS and Android that intercepted traffic to specific subdomains and decrypted it. The internal email described it plainly as a man-in-the-middle approach. That is a named cyberattack technique. Facebook was running it against its own users, on their own phones, to read traffic meant for a competitor.</p>

<p>The programme was called Project Ghostbusters, a joke about Snapchat’s logo. It later expanded to YouTube and Amazon. It ran from roughly June 2016 to May 2019, straight through the publication of the manifesto.</p>

<p>Not everyone inside agreed. Facebook’s then-head of security engineering, Pedro Canahuati, wrote that he could not think of a good argument for why it was acceptable.</p>

<p>The consequences stacked up. Apple pulled Onavo from the App Store in 2018 for breaking privacy rules. Facebook rebranded the effort as “Facebook Research” and paid people aged 13 to 25 to install it. That ended in 2019 after a TechCrunch investigation. In 2024, a federal court in California unsealed the documents during a consumer class action. Plaintiffs argued the conduct was not merely anticompetitive but criminal.</p>

<p>Sources: <a href="https://techcrunch.com/2024/03/26/facebook-secret-project-snooped-snapchat-user-traffic/">TechCrunch on the unsealed documents</a> · <a href="https://www.techradar.com/computing/cyber-security/facebooks-onavo-vpn-used-to-wiretap-competitor-data-court-filings-reveal">TechRadar on the wiretapping claims</a></p>

<p><strong>Why this one matters most.</strong> There was a direct instruction from Zuckerberg. There was a deliberate engineering effort to defeat encryption. Internal objections were raised and overruled. And it was running while the manifesto was being written and published. <a href="https://www.youtube.com/watch?v=SOQynHuWPeI">More on this here</a></p>

<hr />

<h2 id="3-myanmar-and-the-rohingya"><strong>3. Myanmar and the Rohingya</strong></h2>

<p><strong>The promise:</strong> A community that prevents harm and helps during crises.</p>

<p><strong>What happened:</strong> In Myanmar, Facebook was effectively the entire internet. Hate speech against the Rohingya minority spread on the platform with very little moderation. UN investigators found the platform played a “determining role” in the violence. The UN Special Rapporteur said the crisis bore the hallmarks of genocide. Hundreds of thousands of Rohingya fled. One UN investigator said Facebook had “turned into a beast.”</p>

<p>Amnesty International later concluded that Facebook’s own recommendation systems actively amplified the content, and called on Meta to pay reparations.</p>

<p>Sources: <a href="https://time.com/5197039/un-facebook-myanmar-rohingya-violence/">TIME on the UN findings</a> · <a href="https://www.amnesty.org/en/latest/news/2022/09/myanmar-facebooks-systems-promoted-violence-against-rohingya-meta-owes-reparations-new-report/">Amnesty International report</a></p>

<hr />

<h2 id="4-instagram-and-teenagers"><strong>4. Instagram and teenagers</strong></h2>

<p><strong>The promise:</strong> Supportive communities and improved wellbeing.</p>

<p><strong>What happened:</strong> In 2021, whistleblower Frances Haugen leaked years of internal Meta research. It showed the company had studied Instagram’s effect on teenagers and found real harm. Internal figures included 13.5% of UK teen girls saying Instagram worsened suicidal thoughts, and 17% saying it worsened their eating disorders. One internal slide stated plainly that the company made body image issues worse for one in three teen girls.</p>

<p>The research was not published. Meta executives publicly described the effects as small or positive.</p>

<p>Sources: <a href="https://www.npr.org/2021/10/05/1043207218/whistleblower-to-congress-facebook-products-harm-children-and-weaken-democracy">NPR on Haugen’s congressional testimony</a> · <a href="https://www.cbsnews.com/news/facebook-instagram-dangerous-content-60-minutes-2022-12-11/">CBS 60 Minutes</a></p>

<p><em>A caveat worth stating: some researchers argue the leaked studies were small, self-reported, and not designed to be representative. That criticism is fair. It does not change the fact that Meta ran the research, saw the numbers, and said something different in public.</em> Source: <a href="https://www.npr.org/2021/10/06/1043138622/facebook-instagram-teens-mental-health">NPR’s critical read of the same data</a></p>

<hr />

<h2 id="5-election-misinformation"><strong>5. Election misinformation</strong></h2>

<p><strong>The promise:</strong> Accuracy of information matters, and Facebook would work on it.</p>

<p><strong>What happened:</strong> Zuckerberg spent months publicly denying the problem. He called the idea that fake news on Facebook influenced the 2016 election “crazy.” He said Facebook’s own security experts had found no evidence of Russian involvement. Then the company handed Congress roughly 3,000 ads bought by a Russian troll farm, and removed hundreds of linked accounts and pages.</p>

<p>The denial and the manifesto overlap in time. The manifesto was published in February 2017. The admission came that September.</p>

<p>Sources: <a href="https://www.npr.org/sections/alltechconsidered/2017/09/26/553827241/facebook-faces-increasing-scrutiny-over-election-related-russian-ads">NPR on the Russian ads</a> · <a href="https://globalnews.ca/news/3724549/facebook-russian-troll-farm-election-ads/amp/">Global News on the troll farm ad buys</a></p>

<hr />

<h2 id="6-january-6"><strong>6. January 6</strong></h2>

<p><strong>The promise:</strong> Infrastructure that prevents harm and supports civic engagement.</p>

<p><strong>What happened:</strong> In March 2021, Zuckerberg told a House committee that Facebook had done its part to secure election integrity. Weeks later, an internal Facebook report reached the opposite conclusion. It found the company had failed to stop the “Stop the Steal” movement from organising on the platform. The report stated the platform helped incite the Capitol insurrection.</p>

<p>Meta’s spokesman rejected the framing publicly. The report itself was reportedly locked down internally after it leaked.</p>

<p>Sources: <a href="https://www.npr.org/2021/10/22/1048543513/facebook-groups-jan-6-insurrection">NPR on the internal Stop the Steal report</a> · <a href="https://www.buzzfeednews.com/article/craigsilverman/facebook-failed-stop-the-steal-insurrection">BuzzFeed News, which broke the story</a></p>

<hr />

<h2 id="7-concentration-of-power"><strong>7. Concentration of power</strong></h2>

<p><strong>The promise:</strong> The manifesto framed Facebook as a bottom-up layer between individuals and government.</p>

<p><strong>What happened:</strong> The FTC sued Meta in 2020, arguing it had bought Instagram and WhatsApp specifically to neutralise rivals and lock in a monopoly. The case ran five years.</p>

<p>Meta won. In November 2025, Judge James Boasberg ruled that the FTC had failed to prove Meta currently holds a monopoly, largely because TikTok and YouTube now compete with it directly.</p>

<p>This one is genuinely mixed. Meta was not found to have broken the law. But the court explicitly declined to rule on whether Meta held monopoly power <em>at the time</em> of the acquisitions. The question was left open.</p>

<p>Sources: <a href="https://www.npr.org/2025/11/18/nx-s1-5495626/meta-ftc-instagram-whatsapp-antitrust-ruling">NPR on the ruling</a> · <a href="https://www.congress.gov/crs-product/LSB11379">Congressional Research Service summary</a></p>

<hr />

<h2 id="8-ai-chatbots-and-minors-the-most-recent-case"><strong>8. AI chatbots and minors (the most recent case)</strong></h2>

<p>In August 2025, Reuters obtained an internal Meta document called “GenAI: Content Risk Standards.” It stated that it was acceptable for Meta’s chatbots to engage a child in conversations that are romantic or sensual. The document had reportedly been reviewed and approved by Meta’s legal, policy, and engineering staff, including its chief ethicist.</p>

<p>Meta confirmed the document was real. It removed those sections only after Reuters asked about them. US senators opened investigations.</p>

<p>Sources: <a href="https://www.reuters.com/investigates/special-report/meta-ai-chatbot-guidelines/">Reuters investigation</a> · <a href="https://www.cnbc.com/2025/08/29/meta-ai-chatbot-teen-senate-probe.html">CNBC on the Senate probe and Meta’s response</a> · <a href="https://www.markey.senate.gov/imo/media/doc/letter_to_meta_on_ai_chatbots.pdf">Senator Markey’s letter to Meta</a></p>

<hr />

<h1 id="part-two-reading-the-2026-ai-manifesto"><strong>Part Two: Reading the 2026 AI Manifesto</strong></h1>

<h2 id="what-it-says"><strong>What it says</strong></h2>

<p>The new essay runs about 6,500 words. Its core argument is that the real danger of AI is not the technology. It is the concentration of control over the technology. Zuckerberg’s answer is “personal superintelligence”: an AI agent for every individual, aligned to that person’s own goals.</p>

<p>The essay rests on three claims:</p>

<ol>
  <li>Individual empowerment drives prosperity.</li>
  <li>Invention, not automation, is the purpose of superintelligence.</li>
  <li>Balance of power, not technical safeguards, is the foundation of safety.</li>
</ol>

<p>Meta says it will give basic versions away free to billions of people. Advanced features will be priced through what the essay calls a dynamic auction mechanism. Meta is backing this with roughly $145 billion in 2026 capital expenditure, mostly on data centres, plus a $1 billion community fund.</p>

<p>Source: <a href="https://techcrunch.com/2026/08/10/mark-zuckerbergs-ai-manifesto-is-exactly-why-people-dont-like-ai/">TechCrunch’s critical read</a></p>

<h2 id="how-each-claim-serves-meta"><strong>How each claim serves Meta</strong></h2>

<p>This is the part worth sitting with. Every philosophical position in the essay maps onto a commercial or regulatory advantage for Meta.</p>

<p><strong>“Concentration of power is the real risk.”</strong> Meta is behind OpenAI, Anthropic, and Google on frontier model quality. Llama 4 underperformed in 2025. If the race is about who has the best closed model, Meta loses. If the race is about who distributes the most models, Meta’s open-weight strategy wins. The moral argument and the competitive strategy point the same direction.</p>

<p><strong>“Open weights are the safe path.”</strong> Open weights are also the path that undercuts competitors’ pricing power. Giving models away is a viable strategy specifically for a company that makes money from advertising and engagement rather than from selling model access.</p>

<p><strong>“Superintelligence is an invention tool, not a labour replacement.”</strong> This defuses the single most politically dangerous critique of AI: job loss. It is also the claim with the least evidence behind it.</p>

<p><strong>“Regulation should come later, through voluntary checkpoint-sharing.”</strong> Meta proposes sharing intermediate training checkpoints with governments rather than accepting binding pre-deployment rules. That is self-regulation with extra steps. Meta spent over $25 million on US lobbying in 2025.</p>

<p><strong>“Personal AI aligned to your goals.”</strong> Meta’s business model is attention and advertising. An AI assistant embedded in Instagram, WhatsApp, and Facebook is the deepest possible engagement product. “Aligned to your goals” and “optimised for your engagement” are not the same thing, and the essay does not distinguish them.</p>

<p><strong>“Free for billions.”</strong> Free products built Facebook’s user base. The pattern is known and the  cost was paid in data and attention.</p>

<p><strong>To conclude</strong></p>

<p>Meta is not a rogue actor in a healthy market; it is a company doing what the market rewards, at the scale the market allows. Even if Zuckerberg is replaced, the incentive remains. Unfortunately, given the power of AI, this needs to change.</p>

<p>We need systemic change that proactively protects individuals. Considering that governments are only now moving to protect young people from social media, five years after public evidence of the harm it causes (see Meta’s leaked research), waiting that long to regulate AI would be an unprecedented catastrophe. Let alone leaving people like Zuckerberg and Musk to dictate how that regulation should be done.</p>]]></content><author><name>Georgios Lamprakis</name></author><category term="ai" /><summary type="html"><![CDATA[The bullshit in Zuckerberg’s Manifestos]]></summary></entry><entry><title type="html">Commit Things You Learn To Your Long Term Memmory</title><link href="https://mormolis.github.io/commit-things-you-learn-to-your-long-term-memmory.html" rel="alternate" type="text/html" title="Commit Things You Learn To Your Long Term Memmory" /><published>2022-03-20T00:00:00+00:00</published><updated>2022-03-20T00:00:00+00:00</updated><id>https://mormolis.github.io/commit-things-you-learn-to-your-long-term-memmory</id><content type="html" xml:base="https://mormolis.github.io/commit-things-you-learn-to-your-long-term-memmory.html"><![CDATA[<p>I like learning new things. Almost every day I will read a few pages of a technical book, watch a video tutorial or even explore by doing myself. There is a problem though. Even though I understand things and I can fool myself easily that every time I read something I have digested the information and I will be able to recall it should I need to, that’s a lie.
Ask me a week later and I might have forgotten the existence of the topic. Is not-on-demand studying a waste of time then? I would not say so. For me even if the piece of information is completely forgotten, the next time I will have to re-learn it, it will be much easier. Unfortunately, one can admit that this is definitely not the best use of time…</p>

<p>I had accepted my fate. The enjoyment of studying stuff will compromise the side effects of poor memory.</p>

<p>Until I came across <a href="http://augmentingcognition.com/ltm.html">this article by Michael Nielsen</a>. He is talking about systems to improve long-term memory and he is giving his view on one of them. Nielsen, amongst others, is giving his views on ways one can use a piece of software called <a href="https://apps.ankiweb.net/index.html">Anki</a>. He is talking about personal experience, patterns and antipatterns and how making Anki part of his life had managed to enhance his long-term memory on information that is useful for his daily work and personal life.</p>

<p>After I read this article I felt like I had acquired a superpower. I immediately downloaded the app - which is available for all the popular operating systems (Linux, Mac, Windows, Andoid, IOS) and a web app version accessible from everywhere - and waited for the next opportunity to come.</p>

<p>I was inspired by the hints and tips on how to use it by Nielsen but after a few weeks, I finetuned the process for myself.</p>

<p>Below are things that I found useful on using Anki (or ankifying knowledge) which is a mix of advice from the above article, in random order.</p>

<p>But first of all, let’s see what is Anki.</p>

<h2 id="what-is-anki">What is Anki?</h2>
<p>Wikipedia summarises it perfectly:</p>
<blockquote>
  <p>Anki is a free and open-source flashcard program using spaced repetition, a technique from cognitive science for fast and long-lasting memorization. “Anki” is the Japanese word for “memorization”</p>
</blockquote>

<p>In practice, you create flashcards. When you review them, you can rate how easy it was for you to answer the question. The options are:</p>
<ul>
  <li>Again</li>
  <li>Hard</li>
  <li>Good</li>
  <li>Easy</li>
</ul>

<p>Based on your selection, Anki will either display the flashcard again or reschedule it for tomorrow, the day after tomorrow, or after a couple of days. Now, if you have repeatedly rated the flashcard as good or easy the flashcard will take longer to re-appear on your deck. In this way, you can review hundreds of cards in less than half an hour.</p>

<p>As you could tell, the difference between a classical flashcard app and Anki is that Anki will show you cards with information that you will most likely forget, so a quick refresh will extend it in your memory. You will find out that a flashcard might appear again after months if you have rated it easy multiple times.</p>

<h2 id="things-that-worked-for-me">Things that worked for me:</h2>
<ul>
  <li>Once you get your first flashcard created, you will need to establish a habit of spending time with Anki every day. So you should download the app and make use of the idle time that you might have during the day. I initially started having a routine, checking in with Anki every morning before work but that didn’t work all of the time. So while on the go, waiting in a queue or taking a pooh is the perfect time to enhance your long-term memory.</li>
  <li>I have tried ready-made decks (there are thousands available for almost every topic, free to download), but as Nielsen suggests making your own is part of the learning process.</li>
  <li>I separate my decks based on topic (against Nielsen’s advice). I have multiple decks instead of a massive one. This makes it easier for me to review them by making the process less boring. If I feel like I skip a deck at some point. It makes it easier for me to open the app and practice.</li>
  <li>Neilsen suggests not to try to Ankify things that you are not interested in or there is no purpose for them just for the sake of committing it into memory. I can verify that it is indeed a waste of time. I also have an example. I watched a tutorial on Kubernetes and thought that it would be a good idea to ankify it. I did not have a goal at that point in time and I didn’t need K8s knowledge in my daily life. The deck was a nightmare. Every card was reappearing day after day. After a few months, I decided to sit on K8s Certification. I deleted the old deck and as I was going through the learning material I created a new one. The new one works! And that’s because there is a point of me taking time to review the cards. (Brains are lazy and I guess for a good reason…)</li>
  <li>Neilsen advises on making Anki card content atomic. A very simple question with a single answer. I follow this advice most of the time and it works better than having a complex multi-part answer. So try to break a question into simpler ones if you can. It works better.</li>
  <li>Anki gives you the chance to use images on the answer part. That works for me brilliantly</li>
  <li>if you are on IOS, you will find out that the Anki app costs £20 to purchase. Even though it might sound like a lot of money for something that other platforms have for free (I guess that’s the fate of apple products users, to pay for everything). Go ahead and buy it. You support an amazing project and you will be able to practice your flashcards even when you pooh (I think I mentioned that again :P)</li>
</ul>

<p>I think that was it. I have put down everything I could share for making your long-term memory stronger.
<a href="https://apps.ankiweb.net/index.html">Download Anki</a> and enjoy your new superpower!</p>]]></content><author><name>Georgios Lamprakis</name></author><category term="learning" /><summary type="html"><![CDATA[I like learning new things. Almost every day I will read a few pages of a technical book, watch a video tutorial or even explore by doing myself. There is a problem though. Even though I understand things and I can fool myself easily that every time I read something I have digested the information and I will be able to recall it should I need to, that’s a lie. Ask me a week later and I might have forgotten the existence of the topic. Is not-on-demand studying a waste of time then? I would not say so. For me even if the piece of information is completely forgotten, the next time I will have to re-learn it, it will be much easier. Unfortunately, one can admit that this is definitely not the best use of time…]]></summary></entry><entry><title type="html">Containerize Casandra With Schema</title><link href="https://mormolis.github.io/containerize-casandra-with-schema.html" rel="alternate" type="text/html" title="Containerize Casandra With Schema" /><published>2020-05-26T00:00:00+00:00</published><updated>2020-05-26T00:00:00+00:00</updated><id>https://mormolis.github.io/containerize-casandra-with-schema</id><content type="html" xml:base="https://mormolis.github.io/containerize-casandra-with-schema.html"><![CDATA[<p>The problem: We have an application that uses Cassandra as the preferred persistence technology and we would like to have a one-command container running for local development and testing.</p>

<p>In other words, we need the Cassandra container to have the Keyspace and the up-to-date schema following our migration scripts by executing something like  <code class="language-plaintext highlighter-rouge">./run_containerised_cassandra_locally.sh</code>
Breaking this problem into two sub-problems we identify that in order to do that we need a DB migration tool. In RDBMS we have tools like Flyway or Liquibase. For Cassandra, with a little bit of digging, com.hhandoko:cassandra-migration can be found. Which we can be incorporated into our codebase as a dependency or can exist as a standalone artefact (jar file) to be run on demand by a script. The latest is what we are going to use here.
When we go to releases of that repo, we can see the version of the jar with dependencies. (Current versioncassandra-migration-0.17-jar-with-dependencies.jar).</p>

<p>We want to create a structure like the following:</p>

<p><img width="810" alt="image" src="https://user-images.githubusercontent.com/18053538/159135993-194b389d-9dbe-4e14-a143-1e03732b38c6.png" /></p>

<p>Under schema_migration we place another directory which will hold our migration scripts. The naming should be like the one in the screenshot. Starting with V1__ (mind the double underscore) and a description of the script it contains.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">exists</span> <span class="n">our_table</span><span class="p">(</span>
        <span class="n">id</span> <span class="nb">text</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
        <span class="n">name</span> <span class="nb">text</span><span class="p">,</span>
        <span class="n">team</span> <span class="nb">text</span><span class="p">,</span>
        <span class="n">contact_email</span> <span class="nb">text</span><span class="p">,</span>
        <span class="n">created</span> <span class="nb">timestamp</span><span class="p">,</span>
        <span class="n">enabled</span> <span class="nb">boolean</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Now if we want to run the schema migration we need to call the java jar file with some parameters. For the shake of convenience, we introduce the script run.sh that you can see on the screenshot above.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c">#!/usr/bin/env bash</span>

java <span class="nt">-jar</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.scripts.locations<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$MIGRATION_SCRIPT_LOCATION</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.contactpoints<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_CONTACT_POINTS</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.port<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_PORT</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.username<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_USERNAME</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.password<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_PASSWORD</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.keyspace.name<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$KEYSPACE_NAME</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.keyspace.consistency<span class="o">=</span>ALL <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.enablessl<span class="o">=</span><span class="nb">true</span> <span class="se">\</span>
cassandra-migration-0.17-jar-with-dependencies.jar migrate
</code></pre></div></div>

<p>We need to make it runnable with <code class="language-plaintext highlighter-rouge">chmod +x run.sh</code>.</p>

<p>And finally, before we run it, we need to export the variables that are passed as system variables to the migration utility. (For local development that there are no really sensitive values We can keep a file with the commands that we will source to our environment, example to follow.</p>

<p>As a bonus, because We are using the migration job as part of our pipeline, we have containerized the migration utility so we can easily use it on Jenkins. You don’t have to do the next part if you only want this for the local testing/development container.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> library/openjdk:11.0.2-jre</span>

<span class="k">ADD</span><span class="s"> cassandra-migration-0.17-jar-with-dependencies.jar run.sh /etc/app/</span>

<span class="k">RUN </span><span class="nb">mkdir</span> <span class="nt">-p</span> /opt/java/gclogs

<span class="k">ADD</span><span class="s"> migration_scripts /migration_scripts</span>

<span class="k">WORKDIR</span><span class="s"> /etc/app/</span>

<span class="k">EXPOSE</span><span class="s"> 10350</span>

<span class="k">ENTRYPOINT</span><span class="s"> ["/etc/app/run.sh"]</span>
</code></pre></div></div>

<p>Now we are ready to create the Cassandra container with our stuff :D</p>

<p><img width="667" alt="image" src="https://user-images.githubusercontent.com/18053538/159136137-b988704d-f70e-41b9-9566-ea2e60fb7b66.png" /></p>

<p>Now, we are in the local_cassandra directory of the above screenshot. In the dbScripts directory, we just add a simple cql command that will be used to create the keyspace once Cassandra is up and running.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">KEYSPACE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> 
<span class="n">our_keyspace</span> 
<span class="k">WITH</span> <span class="n">replication</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'class'</span><span class="p">:</span><span class="s1">'SimpleStrategy'</span><span class="p">,</span><span class="s1">'replication_factor'</span><span class="p">:</span><span class="s1">'1'</span><span class="p">};</span>
</code></pre></div></div>

<p>Let’s create the docker file now:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> cassandra</span>

<span class="k">COPY</span><span class="s"> set_environment_variables /set_environment_variables</span>
<span class="k">COPY</span><span class="s"> entrypoint-wrap.sh /entrypoint-wrap.sh</span>
<span class="k">EXPOSE</span><span class="s"> 9042</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/entrypoint-wrap.sh"]</span>
<span class="k">CMD</span><span class="s"> ["cassandra", "-f"]</span>
</code></pre></div></div>
<p>On line 3 we copy a file with all variables that the migration script will need. Since we do not have sensitive secrets for the local env we just have the following.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export MIGRATION_SCRIPT_LOCATION="filesystem:../schema_migration/migration_scripts" &amp;&amp; \
export CASSANDRA_DB_CONTACT_POINTS=localhost &amp;&amp; \
export CASSANDRA_DB_PORT=5555 &amp;&amp; \
export CASSANDRA_DB_USERNAME="" &amp;&amp; \
export CASSANDRA_DB_PASSWORD="" &amp;&amp; \
export KEYSPACE_NAME=our_keyspace
export CASSANDRA_DB_SSL_ENABLED=false
</code></pre></div></div>

<p>On line 4 we copy the intermediate script on the container. I call it intermediate because it will run before the real entry point. It is like a wrapper.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>


<span class="c">#to set keyspace name</span>
<span class="nb">source </span>set_environment_variables

<span class="k">if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$KEYSPACE_NAME</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nv">$1</span> <span class="o">=</span> <span class="s1">'cassandra'</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  <span class="c"># Create default keyspace for single node cluster</span>
  <span class="nv">CQL</span><span class="o">=</span><span class="s2">"CREATE KEYSPACE </span><span class="nv">$KEYSPACE_NAME</span><span class="s2"> WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1};"</span>
  <span class="k">until </span><span class="nb">echo</span> <span class="nv">$CQL</span> | cqlsh<span class="p">;</span> <span class="k">do
    </span><span class="nb">echo</span> <span class="s2">"cqlsh: Cassandra is still starting up - please wait - retrying"</span>
    <span class="nb">sleep </span>2
  <span class="k">done</span> <span class="o">&amp;&amp;</span> <span class="nb">touch</span> /tmp/cassandra_up.flag &amp;
<span class="k">fi

</span><span class="nb">exec</span> /docker-entrypoint.sh <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</code></pre></div></div>

<p>It is definitely an interesting one. Let’s analyse its functionality.
On line 5 we source the above file so it can export the variable we set earlier for the migration script. This will make use of the schema name value.
Then it will loop and wait for 2 seconds until the creation of the keyspace will return successfully when it will, it will also create a flag file in <code class="language-plaintext highlighter-rouge">/tmp/cassandra_up.flag</code> folder that we will use to make sure that Cassandra is up and the keyspace has been created. This file will trigger the schema migration process in the script run_containerised_cassandra_locally.sh.  The &amp; at the end of the line 13 makes the above block of code run asynchronously on the background.</p>

<p>On line 16 we proceed with the real docker entry point passing all the arguments that were initially passed on this script on the Dockerfile.</p>

<p>Finally, our script that will orchestrate all the above and create the Cassandra container.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>

<span class="c"># exc1</span>
<span class="nb">echo</span> <span class="s2">"------------- cleaning existing instances running ------------- "</span>
docker stop localCassandra
docker container <span class="nb">rm </span>localCassandra
<span class="c"># end of exc1</span>

<span class="c"># exc2</span>
<span class="nb">echo</span> <span class="s2">"------------- running container in detached mode ------------- "</span>
docker build <span class="nb">.</span> <span class="nt">-t</span> containerised_local_cassandra
docker run <span class="nt">--name</span> localCassandra <span class="nt">-d</span> <span class="nt">-p</span> 5555:9042 containerised_local_cassandra
<span class="c"># end of exc2</span>

<span class="nb">printf</span>  <span class="s2">"waiting to spin up the containers "</span>

<span class="c"># exc3</span>
<span class="k">while</span> <span class="o">[[</span> <span class="si">$(</span>docker <span class="nb">exec</span> <span class="nt">-it</span> localCassandra <span class="nb">ls</span> /tmp/cassandra_up.flag 2&gt;&amp;1 | <span class="nb">grep</span> <span class="s2">"No such file or directory"</span><span class="si">)</span> <span class="o">]]</span><span class="p">;</span>
<span class="k">do
    </span><span class="nb">sleep </span>1
    <span class="nb">printf</span> <span class="nb">.</span>
<span class="k">done</span>
<span class="c"># end of exc3</span>

<span class="nb">printf</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">------------- setting variables for the migration script: ------------- </span><span class="se">\n\n</span><span class="s2">"</span>
<span class="nb">cat</span> ./set_environment_variables
<span class="nb">source</span> ./set_environment_variables

<span class="nb">printf</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">------------- running the schema migration script -------------</span><span class="se">\n\n</span><span class="s2">"</span>
java <span class="nt">-jar</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.scripts.locations<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$MIGRATION_SCRIPT_LOCATION</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.contactpoints<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_CONTACT_POINTS</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.port<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_PORT</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.username<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_USERNAME</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.password<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_PASSWORD</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.keyspace.name<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$KEYSPACE_NAME</span><span class="si">)</span> <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.keyspace.consistency<span class="o">=</span>ALL <span class="se">\</span>
<span class="nt">-Dcassandra</span>.migration.cluster.enablessl<span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$CASSANDRA_DB_SSL_ENABLED</span><span class="si">)</span> <span class="se">\</span>
../schema_migration/cassandra-migration-0.17-jar-with-dependencies.jar migrate
</code></pre></div></div>

<p><strong>exc1:</strong> clean up the already existing Cassandra containers with the name localCassandra so we won’t have any conflicts.</p>

<p><strong>exc2</strong> are running the container.</p>

<p><strong>exc3</strong> are waiting for the flag file to be created. This will determine when the container is ready for the next step.</p>

<p>Finally the Cassandra migration utility (jar) after we source the variables once again.</p>

<p>Don’t forget to make it executable!</p>

<p>Now we can have a Cassandra container with our schema and everything just by running
<code class="language-plaintext highlighter-rouge">./run_containerised_cassandra_locally.sh</code></p>]]></content><author><name>Georgios Lamprakis</name></author><category term="programming" /><summary type="html"><![CDATA[The problem: We have an application that uses Cassandra as the preferred persistence technology and we would like to have a one-command container running for local development and testing.]]></summary></entry><entry><title type="html">Test File Operations With Junit5 And Tempdir</title><link href="https://mormolis.github.io/test-file-operations-with-junit5-and-tempdir.html" rel="alternate" type="text/html" title="Test File Operations With Junit5 And Tempdir" /><published>2020-02-29T00:00:00+00:00</published><updated>2020-02-29T00:00:00+00:00</updated><id>https://mormolis.github.io/test-file-operations-with-Junit5-and-TempDir</id><content type="html" xml:base="https://mormolis.github.io/test-file-operations-with-junit5-and-tempdir.html"><![CDATA[<p>In Junit4 we had TemporaryFolder. In Junit5 the way to test objects that are interacting with the file system it is pretty straightforward with the use of <code class="language-plaintext highlighter-rouge">@TempDir</code>.</p>

<p>To make things easier to understand lets assume that we have a class that reads all the files, directories, sub directories and files in sub directories and gathers information about them in a form of a simple dto that contains the extension, if it is directory, the absolute path etc. and adds them in a list.</p>

<p>So given a Path you get a list of objects that contain the file information.</p>

<p>In order to test this functionality we are going to use the TempDir helper from Junit5.
This will create a temporary directory in our filesystem that will be cleaned automatically when our testing is finished.</p>

<p>Check out the example bellow</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">directory.scanner.attributes.reader</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.io.TempDir</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.io.File</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.IOException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Files</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Path</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">assertj</span><span class="o">.</span><span class="na">core</span><span class="o">.</span><span class="na">api</span><span class="o">.</span><span class="na">Assertions</span><span class="o">.</span><span class="na">assertThat</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">assertj</span><span class="o">.</span><span class="na">core</span><span class="o">.</span><span class="na">api</span><span class="o">.</span><span class="na">Assertions</span><span class="o">.</span><span class="na">assertThatThrownBy</span><span class="o">;</span>

<span class="kd">class</span> <span class="nc">FileAttributeDtoReaderTest</span> <span class="o">{</span>

    <span class="nc">FileAttributeReader</span> <span class="n">fileAttributeReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileAttributeReader</span><span class="o">();</span>

    <span class="nd">@Test</span>
    <span class="kt">void</span> <span class="nf">returnsAListOfAllFilesAndDirectoriesInTheFileTree</span><span class="o">(</span><span class="nd">@TempDir</span> <span class="nc">Path</span> <span class="n">tempDirectory</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>

        <span class="n">createFilesAndDirectoriesInTemporaryDirectory</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">);</span>

        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">AttributeDto</span><span class="o">&gt;</span> <span class="n">actual</span> <span class="o">=</span> <span class="n">fileAttributeReader</span><span class="o">.</span><span class="na">scanFilesInADirectory</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">);</span>

        <span class="n">assertThat</span><span class="o">(</span><span class="n">actual</span><span class="o">).</span><span class="na">contains</span><span class="o">(</span><span class="n">expectedListOfFileAttributes</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">createFilesAndDirectoriesInTemporaryDirectory</span><span class="o">(</span><span class="nc">Path</span> <span class="n">tempDirectory</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
        <span class="nc">Files</span><span class="o">.</span><span class="na">createFile</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"file1.txt"</span><span class="o">));</span>
        <span class="nc">Files</span><span class="o">.</span><span class="na">createFile</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"file2."</span><span class="o">));</span>
        <span class="nc">Files</span><span class="o">.</span><span class="na">createFile</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"file3.txt"</span><span class="o">));</span>
        <span class="nc">Files</span><span class="o">.</span><span class="na">createFile</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"file4"</span><span class="o">));</span>
        <span class="nc">Path</span> <span class="n">dir</span> <span class="o">=</span> <span class="nc">Files</span><span class="o">.</span><span class="na">createDirectory</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"dir"</span><span class="o">));</span>
        <span class="nc">Files</span><span class="o">.</span><span class="na">createFile</span><span class="o">(</span><span class="n">dir</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="s">"fileInDirectory"</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">AttributeDto</span><span class="o">[]</span> <span class="nf">expectedListOfFileAttributes</span><span class="o">(</span><span class="nc">Path</span> <span class="n">tempDirectory</span><span class="o">)</span> <span class="o">{</span>

        <span class="nc">AttributeDto</span> <span class="n">attributeDto0</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">().</span><span class="na">toString</span><span class="o">(),</span> <span class="s">""</span><span class="o">,</span> <span class="mi">4096</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"file1.txt"</span><span class="o">,</span> <span class="s">"txt"</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"file2."</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto3</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"file3.txt"</span><span class="o">,</span> <span class="s">"txt"</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto4</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"file4"</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto5</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"dir"</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="mi">4096</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
        <span class="nc">AttributeDto</span> <span class="n">attributeDto6</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">(</span><span class="n">tempDirectory</span><span class="o">.</span><span class="na">toAbsolutePath</span><span class="o">()</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"dir"</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"fileInDirectory"</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
        <span class="nc">AttributeDto</span><span class="o">[]</span> <span class="n">expected</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AttributeDto</span><span class="o">[</span><span class="mi">7</span><span class="o">];</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto0</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto1</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto2</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">3</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto3</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">4</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto4</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto5</span><span class="o">;</span>
        <span class="n">expected</span><span class="o">[</span><span class="mi">6</span><span class="o">]</span> <span class="o">=</span> <span class="n">attributeDto6</span><span class="o">;</span>
        <span class="k">return</span> <span class="n">expected</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Check on line 20 how we pass the temporary directory as a test parameter annotated accordingly.</p>

<p><code class="language-plaintext highlighter-rouge">@TempDir</code> will create a directory (depending on your OS) on a tmp folder like <code class="language-plaintext highlighter-rouge">/tmp/junit6110933399026626368</code>, but this should not worry us since junit is going to get rid of it as soon as we finish with our test.</p>

<p>Method <code class="language-plaintext highlighter-rouge">createFilesAndDirectoriesInTemporaryDirectory</code> is using the temporary directory along with Files api (java 7) to create folder structure like the following.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tempDir/

  file1.txt
  file2.
  file3.txt
  file4

  dir/
    fileInDirectory
</code></pre></div></div>

<p>Finally the method on line 38 created the expected result. On the <code class="language-plaintext highlighter-rouge">AtributeDto</code> we can spot the absolute path of our generated files being passed as the first parameter. Second parameter is the extension of the file, third is the size and finally a boolean that dictates whether the file is a directory or not.</p>

<p>On this example for the assertions I am using a great assertion library which is called assertJ and can be found <a href="https://assertj.github.io/doc/">here</a>.</p>]]></content><author><name>Georgios Lamprakis</name></author><category term="programming" /><summary type="html"><![CDATA[In Junit4 we had TemporaryFolder. In Junit5 the way to test objects that are interacting with the file system it is pretty straightforward with the use of @TempDir.]]></summary></entry><entry><title type="html">Java Primitives Little Secrets</title><link href="https://mormolis.github.io/java-primitives-little-secrets.html" rel="alternate" type="text/html" title="Java Primitives Little Secrets" /><published>2020-02-22T00:00:00+00:00</published><updated>2020-02-22T00:00:00+00:00</updated><id>https://mormolis.github.io/java-primitives-little-secrets</id><content type="html" xml:base="https://mormolis.github.io/java-primitives-little-secrets.html"><![CDATA[<p>Here is a bunch of java primitives’ interesting behaviour that we ofter ignore or forget! Recap time in 3 - 2 - 1…</p>

<h3 id="data-type-promotions">Data type promotions</h3>
<p>When you try to perform a numerical operation on a short type for example, the result will be promoted to int, therefore it wont compile if you try to store the result in a short variable unless you explicitly cast it (check line x).</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">short</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
<span class="kt">short</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">short</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="o">*</span><span class="n">y</span> <span class="c1">// does NOT compile</span>
<span class="kt">short</span> <span class="n">z</span> <span class="o">=</span> <span class="o">(</span><span class="kt">short</span><span class="o">)(</span><span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="o">)</span> <span class="c1">// does compile</span>

<span class="c1">//////////////////////////////////////</span>

<span class="kt">short</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
<span class="kt">short</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">u</span> <span class="o">=</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span> <span class="c1">// it does</span>

</code></pre></div></div>

<p>The same applies to byte. Adding two numbers of type byte they will automatically be promoted to short.</p>

<p>What about int? Adding two number of type int together wont result to a long promotion (of course)…</p>

<p>Same as int if you try to to add together two float numbers, the result wont be promoted to double.</p>

<p>All the above are great… but… There is an interesting behaviour when you do the following</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">byte</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
<span class="kt">byte</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="o">;</span>
<span class="n">y</span><span class="o">+=</span><span class="n">x</span><span class="o">;</span>
</code></pre></div></div>

<p>the above is perfectly valid, but the bellow results in a compilation error:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">byte</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
<span class="kt">byte</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="o">;</span>
<span class="n">y</span><span class="o">=</span><span class="n">y</span><span class="o">+</span><span class="n">x</span><span class="o">;</span> <span class="c1">// does NOT compile</span>
</code></pre></div></div>

<p>Yes indeed!</p>

<p>an easier one now:
Please welcome the infamous…</p>

<h3 id="numeric-overflow">Numeric Overflow!</h3>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">);</span>
<span class="c1">//output 2147483647</span>

<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>
<span class="c1">//output -2147483648</span>

<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">Integer</span><span class="o">.</span><span class="na">MIN_VALUE</span><span class="o">);</span> 
<span class="c1">//output -2147483647</span>

<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">Integer</span><span class="o">.</span><span class="na">MIN_VALUE</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span> 
<span class="c1">//output 2147483648</span>
</code></pre></div></div>]]></content><author><name>Georgios Lamprakis</name></author><category term="programming" /><summary type="html"><![CDATA[Here is a bunch of java primitives’ interesting behaviour that we ofter ignore or forget! Recap time in 3 - 2 - 1…]]></summary></entry></feed>