THUMBS SHIFT→

このブログは主に親指シフトを用いて書かれています

firebaseをどうテストするか

firebaseをいじってて少しだけ知見がたまってきたので共有します。

前提

ローカルでテストしたいなら、Firebase Local Emulator Suite を使いましょう。 yarn run firebase emulators:startで起動できます。

yarn firebase emulators:exec 'yarn test'ってやると、emulator起動してからテストしてくれます。

設定

これを書いておきましょう。ローカルでの起動でemulatorを使うようになります。create-react-appを使ってるのでNODE_ENVで分岐してますが、別に他のでもいいです。あとポートは自分のに合わせましょう。

const app = firebase.initializeApp(firebaseConfig);
export const db = app.firestore();
if (process.env.NODE_ENV !== "production") {
  db.settings({
    host: "localhost:8080",
    ssl: false,
  });
  app.functions().useFunctionsEmulator("http://localhost:5001");
}

firestore

rulesのテストは簡単です。@firebase/rules-unit-testingを使えば下のようなコードですぐテストできます。(@firebase/testingから改名しました)

import {
  assertFails,
  initializeTestApp,
} from "@firebase/rules-unit-testing";

test("denied if not authed", async () => {
    const notAuthedDB = initializeTestApp({ auth: { uid: undefined } }).firestore();
    const userRef = notAuthedDB.doc("/users/uid");
    await assertFails(userRef.set({ hoge: "hoge" }));
  });

もしrulesを回避して先にテスト用データを用意しておきたいなら、initializeAdminAppを使いましょう*1

firebase.rulesはちゃんと設定している限り自動で読み込まれるので、loadFirestoreRulesbeforeEachで読み込んだりする必要はないはずです。

問題なのが、以下のような関数をテストする場合です。

export const complexTransaction = async () => {
  // 複雑なDB操作...
}

これに関してはスマートな解決策を見つけられていません。一応jestの場合は、

jest.mock("firebase/app", () => {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const { initializeTestApp, firestore } = require("@firebase/testing");
  const mockedApp = initializeTestApp({
    projectId: "pid",
    auth: { uid: "uid" },
  });
  return {
    initializeApp: () => mockedApp,
    firestore: firestore,
  };
});

というようにmockすればなんとかテストできますが、あまり綺麗ではないですね。最悪adminでfirebase.firestoreをemulatorにつなげてくれればいいんですが……。

functions

import * as admin from "firebase-admin";
import * as funcTest from "firebase-functions-test";

const db = admin.firestore();
const tester = funcTest({ projectId: process.env.GCLOUD_PROJECT });
const makeDocumentSnapshot = tester.firestore.makeDocumentSnapshot;
const makeChange = tester.makeChange;

const wrapped = tester.wrap(yourAwesomeFunction);
test("assert Firestore", async () => {
  // arrange
  const roomId = "sefasef";
  const roomPath = `rooms/${roomId}`;
  const beforeData = {
    hoge: "hoge",
  };
  const before = makeDocumentSnapshot(beforeData, roomPath);
  const afterData = {
    hoge: "fuga",
  };
  const after = makeDocumentSnapshot(afterData, roomPath);
  const change = makeChange(before, after);
  // act
  await wrapped(change, { params: { roomId } });
  // assert
  const roomRef = db.doc(roomPath);
  expect((await roomRef.get()).data()).toEqual(afterData);
});

こんな感じでいけます。functionsのテストは比較的簡単です。あと、Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may failと警告がでます。これは正直わからん。。。

*1:initializeTestAppのprojectIdと同じもので初期化する必要があります

firebase emulatorにauthenticationはまだ無いよ

https://github.com/firebase/firebase-tools/issues/1677

しばらくfirebase authをエミュレートする方法探してたけど、上のissueにあるように、まだ実装されてない。

やる気は有るようなので待つしかないなーというかんじ

でもログイン回りのテストどうしようか・・・

VS Code Server for WSL closed unexpectedly. Check WSL terminal for more details. というエラーが出る。

vscode+wsl2でモダン開発しよwってなったけどエラー出た。 ggるとPATHの設定だったり、proxyだったりがでてくるけどこれはWSL2になって改善されたっぽい(?)

PS > wsl
$: cd
~$: ls -a ←これで.vscode-server/がでてくるはず
~$: mv .vscode-server/ vscodeserver.backup
~$: code .

vscode-serverが再設定されるので治った。.backupつけた奴は様子見て消していいんじゃないでしょうか。

AVIOT TE-D01g のペアリング方法

左右分離式なので、結構ペアリングが上手くいかなかったりしてめんどいので、うまくいく経験的なやり方を書きます。

  1. 親機からペアリング解除し、イヤホンと親機の電源を切る(二回効果音が鳴るまでボタン長押しで電源off)
  2. イヤホンを左右ともペアリング状態にする(3回なるまで長押しでペアリング状態)
  3. 両方ともペアリングする。(説明書には片方でいいと書いてあったはず)

これが一番安定する気がします。

ちなみに再ペアリングすると、同じデバイスでも2デバイス扱いになるので、最後から3回目につなげたデバイスがイヤホンのメモリから削除されます。

片方だけペアリングしてもうまくいかないのはなぜ~~~~。

追記

両方電源ON→片方だけペアリング でも上手くいくことが最近判明しました。よかったですね。

Windows 10 EducationでMicrosoft Storeを利用する

大学配布のwinライセンスを使っているのでEducationになっているのですが、大学側で一部の利用が制限されている(?)ようで、レポート中でもwindows updateで強制再起動させられたりしています。

中でも一番困ったのが、Microsoft Storeが使えないことで、待ちに待ったWSL2とwindows terminalを入れられないというもどかしい思いをしていました。調べてみると一応解決したのでのせておきます。これがほかの人の環境でも有効なのかはわかんないです。

docs.microsoft.com

ここを見ましょう。 簡単に言うと、

  1. https://educationstore.microsoft.com/ja-jp/storeに行く。
  2. 学校のために購入タブからアプリを検索。(Ubuntuなど)
  3. アプリの入手をクリック
  4. 「インベントリに追加されました」と出るので、インストールをクリック
  5. Microsoft Storeを開く」をクリック

でできると思います。

注意として、microsoft storeのアカウントなのですが、これは自分のアカウントではなく、大学や会社など、組織のアカウントじゃないとだめです。大学の場合はwinのライセンスもらうときとかに配布されるんじゃないかなと思います。忘れたら・・・うん。

Djangoの設定のベタープラクティス

Djangoをデプロイする時の設定です。ベストプラクティスじゃなくて(そのままよりは)ベタープラクティスです

絶対に漏れ、抜けがあるので参考程度に見てください。公式を見るのが最も安全です。

settings.pyの分割

Djangoの本番/開発設定を切り分けるにはsettings.pyを開発用、本番用に分割するのが良いようです。実際DjangoCMSであるWagtailもそのような構成になっています。

普通にDjangoでプロジェクトを作ると、

$ django-admin startproject myprj
$ tree myprj

myprj/
├── manage.py
└── myprj
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

となりますが、Wagtailでは

$ wagtail start myprj
$ tree myprj
myprj/
├── manage.py
├── myprj/
│   ├── __init__.py
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── dev.py
│   │   └── production.py
│   ├── urls.py
│   └── wsgi.py

というように、共通の設定(base.py)を用意して、開発用(dev.py)と本番用(production.py)で設定を切り替えています。

そもそもなんで開発/本番で設定を切り替えたいかというと、

  • 本番ではDEBUG=Falseにする
  • SECRET_KEYを隠したい
  • ALLOWED_HOSTSを設定する

など、開発/本番で違う点があるためです。

settingsの中身

具体的に、dev.pyは以下のようになっています。

from .base import *

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'o4ei=g0^4cu88sw$@rwuy*gh!4lu7ex74#0t!h^0=fy2k$4-(2'

# SECURITY WARNING: define the correct hosts in production!
ALLOWED_HOSTS = ['*'] 

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'


# ここはよくわかんない。ローカル専用の設定かな?
try:
    from .local import *
except ImportError:
    pass

前述したとおり DEBUG=Trueなのと、ALLOWED_HOSTS=['*']です。 それにSECRET_KEYが直書きですね。

コメントにSECURITY_WARNINGとあるように、この設定は本番で使ってはいけません。また、SECRET_KEYを公にしてもいけません。もしGithubなどに公開してしまったら、別の値に変えた上で、dev.pyをリポジトリから消しましょう*1

ちなみにdev.pyのファイル名についてですが、ここにあるように、local_settings.pyとするのが一般的なようです。

つぎにproduction.pyです

from .base import *

DEBUG = False

try:
    from .local import *
except ImportError:
    pass

これだけ。もはや何もかいてないです。ちゃんと自分で設定しろということでしょう。

  • DEBUG=False
  • SECRET_KEY
  • DATABASE
  • ALLOWED_HOSTS
  • STATIC_ROOT
  • STATIC_URL

とかを設定すれば動くはずです。多分。

なお、Herokuにデプロイする時はdjango-herokuがよしなにやってくれますが、動作を知りたい人は以前の記事で書いたので読んでみてください。

arark.hatenadiary.jp

settingsを使うには

多分分割しただけだとpython manage.py <command>したときにファイルが見つかんないよ的なエラーが出るとおもいます。

manage.pyを以下のように変えてください。

# もともとこうなっているはずなので
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myprj.settings')

# このように設定ファイルの相対パスを指定する
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myprj.settings.dev")

dev/productionはやはり開発/本番で変えるはずなので、try-catchで、dev.pyがあればそっちを使うなどのように条件分岐させてみてください。

ちなみにwsgi.pyやasgi.pyなども同じように設定する必要があります。

*1:それがセキュリティ的に正しいのかはわかりませんが...

Folding@homeに参加しました。手順とトラブルシューティング。

Folding@home(fah)に参加しました。化石と化していた1050tiを使っています。

Folding@homeとは、タンパク質構造予測に必要な計算処理を、世界中の有志による分散コンピューティングで推進するプロジェクトです。 10年以上昔の話になりますが、PlayStation 3を活用したプロジェクトを覚えている方も多いかと思います。

Folding@homeを通じてCOVID-19治療薬の発見に貢献します - Cybozu Inside Out | サイボウズエンジニアのブログ

参加方法

僕はubuntuを使っているので、MacWindowsはあんまりわかんないです。多分インストールして、Foldで出来ると思います。

  1. ここから自分にあったインストーラーをDLする。
  2. Linux系は最低限、fahclientとfahcontrolをDLする。fahviewerはなくても良い。(多分)
  3. statusタブを開いて、Foldをクリックする。

FAQ

client, control, viewerの違いはなんですか?

それぞれが左のソフトウェアに依存しています(多分)。clientがコアソフトウェアで、controlが制御用、viewerでこんなのが見れます。 f:id:arark:20200328194236p:plain

GPUが使われないのですが?

まずドライバをインストールしましょう。

次に

sudo apt install ocl-icd-opencl-dev

してください。

そして、 clientを起動->configure->Expert->Extra Client Optionsの、gpuがfalseになっているならtrueにする->再起動 をやってください。

これでGPUを使ってFoldできるならOK。出来ないならconfigure -> Slots -> Folding Slots-> 追加 ->gpuを試してみてください。

これでも出来ないならわからないです...

GPUのステータスを見たい

nvidia-smi -l 3600

で1時間ごとに使用率とか、温度とかが見れます。

Foldが始まりません

結構中央サーバーにアクセスが集中しているようで、必要なデータをDLするまで時間がかかります。放置してると始まるかも。

ERROR:WU01:FS01:Exception: Could not get an assignmentってエラーがでたりします。

何を計算しているんですか?

解決できません!

公式フォーラムに行きましょう。

ちなみに、

EFLOPSレベルまでいったそうです。

ReactをHerokuに秒速でデプロイしたい

create-react-appで作ったプロジェクトに限った話です!!!!!!!

github.com

ここに書いてあるまんまですが、

APP_NAME=すごいアプリの名前
npx create-react-app $APP_NAME
cd $APP_NAME
heroku create $APP_NAME --buildpack mars/create-react-app
git push heroku master
heroku open

--buildpack mars/create-react-appが一番大事なところなので忘れないように。ちなみに既にあるHerokuアプリでcreate-react-appを使いたい時は、

heroku buildpacks:set mars/create-react-app

です*1

django-herokuは何をやっているのか

django-herokuに頼らず自分で設定していきたいので、まずはdjango-herokuが何をやっているか確認していきます。

github.com

環境変数という語がでてきますが、これはherokuのダッシュボードもしくはCLIから割り当てられる変数で、(おそらく)セキュアです。os.environ[<key>]で取得できます。

49行目 にsettings関数が有りますね。この中を読んでいけばよさそうです。

まず引数ですが、configにはlocals()が渡されるので、つまりはsettings.pyの名前空間です。

53行目からはデータベースの設定のようです。

loggerを飛ばして見ていくと、75行目で大事な設定をしているのがわかります。

 config['DATABASES']['default'] = dj_database_url.config(conn_max_age=conn_max_age, ssl_require=True)

ここでdj_database_url.configという関数がでてきますが、これは環境変数の$DATABASE_URLをURLとか、パスワードとかの部分に分解して、djangoの設定に合うようにパースしてくれています。

これでデータベースは終わりです。

次に92行目からstaticfileの設定をしているようです。

95行目からは以下のようなコードです。

        config['STATIC_ROOT'] = os.path.join(config['BASE_DIR'], 'staticfiles')
        config['STATIC_URL'] = '/static/'


        # Ensure STATIC_ROOT exists.
        os.makedirs(config['STATIC_ROOT'], exist_ok=True)


        # Insert Whitenoise Middleware.
        try:
            config['MIDDLEWARE_CLASSES'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE_CLASSES']))
        except KeyError:
            config['MIDDLEWARE'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE']))


        # Enable GZip.
        config['STATICFILES_STORAGE'] = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

STATIC_ROOTとSTATIC_URLの設定をして、STATIC_ROOTのフォルダを作っていますね。

そのあとwhitenoise*1をmiddlewareに追加して、STATICFILES_STORAGEもwhitenoiseのものに差し替えています。

110行目からはALLOWED_HOSTSです。

    if allowed_hosts:
        logger.info('Applying Heroku ALLOWED_HOSTS configuration to Django settings.')
        config['ALLOWED_HOSTS'] = ['*']

あーよくない。ALLOWED_HOSTS=['*']にしているのでよくないです。なにが良くないのかはわかんないです。

テスト環境ならこれでも良いでしょうが、本番の場合はちゃんとホストするアドレスを指定しておきたいですね。

114行目からはロガーの設定なので割愛。

151行目以下でSECRET_KEYです。

    if secret_key:
        if 'SECRET_KEY' in os.environ:
            logger.info('Adding $SECRET_KEY to SECRET_KEY Django setting.')
            # Set the Django setting from the environment variable.
            config['SECRET_KEY'] = os.environ['SECRET_KEY']

環境変数から取ってきたSECRET_KEYをconfigに割り当てています。間違ってもSECRET_KEYを公開しないようにしましょう。

これをsettings.pyに持ってくればherokuにデプロイできます。

まとめると

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
MIDDLEWARE = [
    # ...
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # ...
]


DATABASES = {
    'default': dj_database_url.config(conn_max_age=CONN_MAX_AGE, ssl_require=True)
}

SECRET_KEY = os.environ.get("SECRET_KEY")

のようになります。whitenoiseのインストールや、環境変数の割当を忘れないでください。

*1:nginxみたいなもの。静的ファイルを配信する。djangoが静的ファイルを配信するより高速

最近やったこと

結構ネタバレがある

読んだ本と見た映画

kindleでハヤカワセールやってるのでSFを読んでる。kindleやばい。いつでも読めるのでいつでも読むことが出来る。

夏への扉

夏への扉

猫の描写が良かった。飼ったこと無いけどあるある〜〜〜〜。てなった。

典型的なタイムトラベル物で、最後に伏線を全部回収していくのはやはり爽快。最後がハッピーエンドなので良い。頑張った後に金と女に恵まれる話っていいよね。

序盤はコールドスリープしか出てこなかったのでどうなるんだろうな〜と思ってたら途中からタイムマシンが出てきた。いやそれ最初からコールドスリープいらなかったのでは。

古典SFあるあるだけど、記述が冗長。刊行当初なら新鮮味があるだろう描写も、もはや現実の方が追い越しているので目新しさはない。

思弁系か娯楽系かでいうと圧倒的に娯楽系だったのでよみやすかった。

アメリカにアンクル・サムってふりがな振ってあるの格好良かったのでどこかで使っていきたい。

ソラリス (ハヤカワ文庫SF)

ソラリス (ハヤカワ文庫SF)

夏への扉と真反対の思弁系。最初の、お客さんの正体がわかるまではホラーとしてめちゃめちゃ楽しかった。わかってからは一気にSpeculative Fictionと化した。

ソラリス学はまぁ冗長だけど必要ではあると思う。あれのおかげでソラリスの特異性が強調される。

この本は様々な読み方をすることが出来る傑作SFというのが一般的な評価らしい。円城塔のいうところの、全てを説明しうるがゆえに何も説明できない概念というのを絡めてなにかもっともらしく論評しようとしたけどどうにもならなかったので辞めた。

スナウトとサルトリウスのお客さんは誰だったのかな。スナウトは動物っぽいと思う。

解説がちゃんと解説してて本当に良かった。解説とタイトルをつけてポエム書いてる著者まじで辞めてくれ。

帰ってきたヒトラー(字幕版)

帰ってきたヒトラー(字幕版)

  • 発売日: 2016/12/23
  • メディア: Prime Video

衝動的に見てみた。勿論原典ではないけど、再現されたヒトラーを見ていて、やっぱ演説が上手いんだなーとか思った。

序盤はタダのコメディで、後半にかけて雲行きが怪しくなっていくという展開も好き。

作中のヒトラー自身は何ら具体的な計画を語ってはいないけど、あまりに演説が上手いので自分の意見を代弁してくれている気がしてきた。

民主主義は国民の不断の努力によって維持されていかなければ行けないんだなぁというお気持ち。

本を読むのは良いけど、構造的に展開を把握できてないよなぁと思ったので読んだ。面白さを作るにはどんでん返しが必須というのは納得。あとは主人公の設定の仕方とか、敵の作り方とか。

純文学とか思弁SFとか、この本の解説から逸脱してるようなのもよくあるが、そういう小説を書くのはめちゃめちゃ難しいだろうな。

確定申告で悩んだので。街には沢山の店があるから開業は簡単そうに思ってたけど、実際は複雑だとわかった。あんなに店があることが信じられない。あと渋谷でどうやってテナント料払ってるのかわからない店だらけなの謎すぎる。

FGOでよく出てくるから読んだらいきなりアイアイエーイベ始まってびっくりした。読めば読むほど神が人間の味方だったことなんて一度もありませんよという気持ちになる。狂ってる?それ誉め言葉ね。

イアソンってすごい人なんだなとか、ヘラクレスは世界一強いんだから!とか、ゼウスカスすぎやろとか思うところが多い。あとはケルト神話北欧神話と日本の偉人とアーサー王伝説とインドと中国とその他諸々の物語を読まなきゃいけない......

ちなみにkindleで読んだけど固定レイアウトだったのが辛かった。

やってること

  • バイトを始めて病んでる
  • django+react+graphQLでSPA作ろうと思ってるけど、ルーティングとかデプロイとかセキュリティで詰まってる。
  • 運転の練習をしてる